rails_execution 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.ruby-version +1 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +129 -0
- data/LICENSE.txt +21 -0
- data/README.md +35 -0
- data/Rakefile +3 -0
- data/app/assets/images/executions/approved.png +0 -0
- data/app/assets/images/executions/favicon.png +0 -0
- data/app/assets/images/executions/logo.png +0 -0
- data/app/assets/images/executions/rejected.png +0 -0
- data/app/assets/images/executions/robot.png +0 -0
- data/app/assets/javascripts/executions/ace.min.js +1 -0
- data/app/assets/javascripts/executions/ace.modes.ruby.min.js +1 -0
- data/app/assets/javascripts/executions/ace.theme-solarized_dark.min.js +1 -0
- data/app/assets/javascripts/executions/base.js +1 -0
- data/app/assets/javascripts/executions/bootstrap.5.2.1.min.js +6 -0
- data/app/assets/javascripts/executions/chart.min.js +13 -0
- data/app/assets/javascripts/executions/comments.js +43 -0
- data/app/assets/javascripts/executions/easymde.min.js +7 -0
- data/app/assets/javascripts/executions/events.js +8 -0
- data/app/assets/javascripts/executions/highlight.min.js +1198 -0
- data/app/assets/javascripts/executions/highlight.ruby.min.js +51 -0
- data/app/assets/javascripts/executions/jquery-3.6.1.min.js +2 -0
- data/app/assets/javascripts/executions/marked.min.js +6 -0
- data/app/assets/javascripts/executions/rails.js +565 -0
- data/app/assets/javascripts/executions/select2.min.js +2 -0
- data/app/assets/stylesheets/executions/base.css +3 -0
- data/app/assets/stylesheets/executions/bootstrap-icons.css +1869 -0
- data/app/assets/stylesheets/executions/comments.css +14 -0
- data/app/assets/stylesheets/executions/easymde.min.css +7 -0
- data/app/assets/stylesheets/executions/fonts.css +4 -0
- data/app/assets/stylesheets/executions/highlight.min.css +9 -0
- data/app/assets/stylesheets/executions/modify.scss +278 -0
- data/app/assets/stylesheets/executions/select2.min.css +1 -0
- data/app/controllers/rails_execution/base_controller.rb +16 -0
- data/app/controllers/rails_execution/comments_controller.rb +34 -0
- data/app/controllers/rails_execution/dashboards_controller.rb +27 -0
- data/app/controllers/rails_execution/tasks_controller.rb +175 -0
- data/app/helpers/rails_execution/base_helper.rb +14 -0
- data/app/helpers/rails_execution/policy_helper.rb +64 -0
- data/app/helpers/rails_execution/rendering_helper.rb +94 -0
- data/app/models/rails_execution/activity.rb +8 -0
- data/app/models/rails_execution/comment.rb +8 -0
- data/app/models/rails_execution/task.rb +71 -0
- data/app/models/rails_execution/task_review.rb +14 -0
- data/app/views/layouts/execution.html.haml +16 -0
- data/app/views/rails_execution/comments/_comment.html.haml +23 -0
- data/app/views/rails_execution/comments/create.js.haml +6 -0
- data/app/views/rails_execution/comments/update.js.haml +2 -0
- data/app/views/rails_execution/dashboards/charts/_insights.html.haml +27 -0
- data/app/views/rails_execution/dashboards/home.html.haml +24 -0
- data/app/views/rails_execution/dashboards/insights.json.jbuilder +8 -0
- data/app/views/rails_execution/shared/_flash.html.haml +7 -0
- data/app/views/rails_execution/shared/_header.html.haml +13 -0
- data/app/views/rails_execution/shared/_paging.html.haml +35 -0
- data/app/views/rails_execution/tasks/_actions.html.haml +12 -0
- data/app/views/rails_execution/tasks/_activities.html.haml +22 -0
- data/app/views/rails_execution/tasks/_attachment_file_fields.html.haml +3 -0
- data/app/views/rails_execution/tasks/_attachment_files.html.haml +12 -0
- data/app/views/rails_execution/tasks/_comments.html.haml +15 -0
- data/app/views/rails_execution/tasks/_form.html.haml +55 -0
- data/app/views/rails_execution/tasks/_form_scripts.html.haml +34 -0
- data/app/views/rails_execution/tasks/_new_comment.html.haml +8 -0
- data/app/views/rails_execution/tasks/_reviewers.html.haml +23 -0
- data/app/views/rails_execution/tasks/_script_content.html.haml +15 -0
- data/app/views/rails_execution/tasks/_show_scripts.html.haml +27 -0
- data/app/views/rails_execution/tasks/_status.html.haml +19 -0
- data/app/views/rails_execution/tasks/_task.html.haml +18 -0
- data/app/views/rails_execution/tasks/_tips.html.haml +18 -0
- data/app/views/rails_execution/tasks/closed.html.haml +12 -0
- data/app/views/rails_execution/tasks/completed.html.haml +12 -0
- data/app/views/rails_execution/tasks/edit.html.haml +3 -0
- data/app/views/rails_execution/tasks/index.html.haml +12 -0
- data/app/views/rails_execution/tasks/new.html.haml +3 -0
- data/app/views/rails_execution/tasks/show.html.haml +38 -0
- data/config/routes.rb +26 -0
- data/lib/generators/rails_execution/file_upload_generator.rb +18 -0
- data/lib/generators/rails_execution/install_generator.rb +36 -0
- data/lib/generators/rails_execution/templates/config.rb.tt +63 -0
- data/lib/generators/rails_execution/templates/file_reader.rb.tt +14 -0
- data/lib/generators/rails_execution/templates/file_uploader.rb.tt +11 -0
- data/lib/generators/rails_execution/templates/install.rb.tt +48 -0
- data/lib/rails_execution/app_model.rb +14 -0
- data/lib/rails_execution/config.rb +73 -0
- data/lib/rails_execution/engine.rb +24 -0
- data/lib/rails_execution/error.rb +6 -0
- data/lib/rails_execution/files/reader.rb +49 -0
- data/lib/rails_execution/files/uploader.rb +42 -0
- data/lib/rails_execution/services/approvement.rb +41 -0
- data/lib/rails_execution/services/execution.rb +87 -0
- data/lib/rails_execution/services/executor.rb +24 -0
- data/lib/rails_execution/services/paging.rb +35 -0
- data/lib/rails_execution/services/syntax_checker.rb +38 -0
- data/lib/rails_execution/version.rb +5 -0
- data/lib/rails_execution.rb +24 -0
- metadata +157 -0
@@ -0,0 +1,94 @@
|
|
1
|
+
module RailsExecution
|
2
|
+
module RenderingHelper
|
3
|
+
|
4
|
+
def render_user_info(user, avatar_size: '40x40')
|
5
|
+
content_tag :div, class: 'user-info' do
|
6
|
+
concat render_owner_avatar(user, size: avatar_size)
|
7
|
+
concat render_owner_name(user)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def render_owner_avatar(owner, size: '32x32')
|
12
|
+
return nil if owner.blank?
|
13
|
+
|
14
|
+
avatar_url = RailsExecution.configuration.owner_avatar.call(owner)
|
15
|
+
return nil if avatar_url.blank?
|
16
|
+
|
17
|
+
image_tag avatar_url, size: size, class: 'bd-placeholder-img flex-shrink-0 me-2 rounded'
|
18
|
+
end
|
19
|
+
|
20
|
+
def render_owner_name(owner)
|
21
|
+
return nil if owner.blank?
|
22
|
+
return nil if RailsExecution.configuration.owner_name_method.blank?
|
23
|
+
|
24
|
+
content_tag :span, owner.public_send(RailsExecution.configuration.owner_name_method)
|
25
|
+
end
|
26
|
+
|
27
|
+
def render_notification_message(mode, message)
|
28
|
+
case mode
|
29
|
+
when 'alert'
|
30
|
+
content_tag :div, class: 'alert alert-warning align-items-center' do
|
31
|
+
concat content_tag(:i, nil, class: 'bi bi-x-octagon mr-2')
|
32
|
+
concat content_tag(:span, message, class: 'ms-2')
|
33
|
+
end
|
34
|
+
when 'notice'
|
35
|
+
content_tag :div, class: 'alert alert-success align-items-center' do
|
36
|
+
concat content_tag(:i, nil, class: 'bi bi-check-circle mr-2')
|
37
|
+
concat content_tag(:span, message, class: 'ms-2')
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def task_reviewed_status(task)
|
43
|
+
@task_reviewed_status ||= {}
|
44
|
+
@task_reviewed_status[task] ||= task.task_reviews.find_by(owner_id: current_owner&.id)&.status&.inquiry
|
45
|
+
end
|
46
|
+
|
47
|
+
def re_page_actions(&block)
|
48
|
+
content_for :page_actions do
|
49
|
+
content_tag :span, class: 'ms-2' do
|
50
|
+
capture(&block)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def re_card_content(id: nil, &block)
|
56
|
+
content_tag :div, id: id, class: 'my-3 p-3 bg-body rounded shadow-sm' do
|
57
|
+
capture(&block)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def re_render_paging(relation)
|
62
|
+
render partial: 'rails_execution/shared/paging', locals: { page: relation.re_current_page, total_pages: relation.re_total_pages }
|
63
|
+
end
|
64
|
+
|
65
|
+
def re_attachment_file_acceptable_types
|
66
|
+
::RailsExecution.configuration.acceptable_file_types.keys.join(',')
|
67
|
+
end
|
68
|
+
|
69
|
+
def re_get_file_name(url)
|
70
|
+
URI(url).path.split('/').last
|
71
|
+
end
|
72
|
+
|
73
|
+
def re_badge_color(status)
|
74
|
+
color = status_to_color(status.to_s.downcase.to_sym)
|
75
|
+
content_tag :small, status.to_s.titleize, class: "fw-light badge rounded-pill text-bg-#{color}"
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def status_to_color(text)
|
81
|
+
{
|
82
|
+
bad: 'danger',
|
83
|
+
good: 'success',
|
84
|
+
closed: 'danger',
|
85
|
+
created: 'primary',
|
86
|
+
rejected: 'danger',
|
87
|
+
approved: 'success',
|
88
|
+
reviewing: 'secondary',
|
89
|
+
completed: 'success',
|
90
|
+
}[text] || 'secondary'
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
class RailsExecution::Task < RailsExecution::AppModel
|
2
|
+
|
3
|
+
PROCESSING_STATUSES = %w(created reviewing approved rejected)
|
4
|
+
|
5
|
+
has_many :activities, class_name: 'RailsExecution::Activity'
|
6
|
+
has_many :comments, class_name: 'RailsExecution::Comment'
|
7
|
+
has_many :task_reviews, class_name: 'RailsExecution::TaskReview'
|
8
|
+
attr_accessor :reviewer_ids
|
9
|
+
|
10
|
+
validates :title, presence: true
|
11
|
+
validates :status, presence: true
|
12
|
+
|
13
|
+
enum status: {
|
14
|
+
created: 'created',
|
15
|
+
reviewing: 'reviewing',
|
16
|
+
approved: 'approved',
|
17
|
+
rejected: 'rejected',
|
18
|
+
completed: 'completed',
|
19
|
+
closed: 'closed',
|
20
|
+
}, _prefix: :is
|
21
|
+
|
22
|
+
enum syntax_status: {
|
23
|
+
bad: 'bad',
|
24
|
+
good: 'good',
|
25
|
+
}, _prefix: true
|
26
|
+
|
27
|
+
scope :processing, -> { where(status: PROCESSING_STATUSES) }
|
28
|
+
|
29
|
+
before_update :re_assign_status
|
30
|
+
after_commit :create_activity, on: :create, if: :owner
|
31
|
+
|
32
|
+
def in_processing?
|
33
|
+
PROCESSING_STATUSES.include?(self.status)
|
34
|
+
end
|
35
|
+
|
36
|
+
def assign_reviewers(ids)
|
37
|
+
ids.each do |id|
|
38
|
+
next if id.blank?
|
39
|
+
|
40
|
+
task_review = self.task_reviews.find_or_initialize_by({
|
41
|
+
owner_id: id,
|
42
|
+
owner_type: ::RailsExecution.configuration.owner_model.to_s,
|
43
|
+
})
|
44
|
+
task_review.status ||= :reviewing
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def reviewer_ids
|
49
|
+
self.task_reviews.pluck(:owner_id)
|
50
|
+
end
|
51
|
+
|
52
|
+
def add_files(attachments, current_owner)
|
53
|
+
::RailsExecution.configuration.file_uploader.new(self, attachments, owner: current_owner).call
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def create_activity
|
59
|
+
self.activities.create(owner: self.owner, message: 'Created the task')
|
60
|
+
end
|
61
|
+
|
62
|
+
def re_assign_status
|
63
|
+
return if self.is_completed? || self.is_closed?
|
64
|
+
|
65
|
+
if self.is_approved? && self.script_changed?
|
66
|
+
self.status = :reviewing
|
67
|
+
self.task_reviews.update_all(status: :reviewing)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class RailsExecution::TaskReview < RailsExecution::AppModel
|
2
|
+
|
3
|
+
belongs_to :task
|
4
|
+
|
5
|
+
validates :task, presence: true
|
6
|
+
validates :status, presence: true
|
7
|
+
|
8
|
+
enum status: {
|
9
|
+
reviewing: 'reviewing',
|
10
|
+
approved: 'approved',
|
11
|
+
rejected: 'rejected',
|
12
|
+
}, _prefix: :is
|
13
|
+
|
14
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
!!!
|
2
|
+
%html
|
3
|
+
%head
|
4
|
+
%title Rails Execution
|
5
|
+
%meta{ name: :viewport, content: 'width=device-width, initial-scale=1' }
|
6
|
+
= csrf_meta_tags
|
7
|
+
= favicon_link_tag 'executions/favicon.png'
|
8
|
+
= stylesheet_link_tag 'https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/css/bootstrap.min.css'
|
9
|
+
= stylesheet_link_tag 'executions/base'
|
10
|
+
= javascript_include_tag 'executions/base'
|
11
|
+
|
12
|
+
%body.bg-light.fw-light.pb-5
|
13
|
+
= render partial: 'rails_execution/shared/header'
|
14
|
+
%main.container
|
15
|
+
= render partial: 'rails_execution/shared/flash'
|
16
|
+
= yield
|
@@ -0,0 +1,23 @@
|
|
1
|
+
- comment_key = comment.cache_key.parameterize(separator: '_').remove('-')
|
2
|
+
|
3
|
+
.media.media-comment.d-flex{ id: comment_key }
|
4
|
+
= render_owner_avatar(comment.owner, size: '50x50')
|
5
|
+
.media-body.rounded.shadow-sm.g-bg-secondary.g-pa-15.w-100
|
6
|
+
- if comment.owner == current_owner
|
7
|
+
= link_to '#edit-comment', class: 'edit-comment' do
|
8
|
+
%i.bi.bi-gear.fs-5.text-dark
|
9
|
+
.review-comment-content
|
10
|
+
.g-mb-15.mb-2
|
11
|
+
.user-info.g-color-gray-dark-v1.mb-0= render_owner_name(comment.owner)
|
12
|
+
.fs-13.text-muted= comment.created_at.strftime('%Y/%m/%d %H:%M')
|
13
|
+
%p.content-markdown= comment.content
|
14
|
+
%p.content-html.normal-text
|
15
|
+
.edit-comment-form.hidden
|
16
|
+
= form_for comment, url: task_comment_path(current_task, comment, comment_key: comment_key), remote: true do |f|
|
17
|
+
= f.text_area :content, { id: "#{comment_key}_content" }
|
18
|
+
.mt-3.text-end
|
19
|
+
%a.btn.btn-outline-default.text-danger.cancel-edit-mode{ href: '#cancel-edit-form' } Cancel
|
20
|
+
= f.submit 'Update', class: 'btn btn-outline-success'
|
21
|
+
|
22
|
+
:javascript
|
23
|
+
window.comments.renderMarkdown("##{comment_key}");
|
@@ -0,0 +1,6 @@
|
|
1
|
+
- if @new_comment.errors.empty?
|
2
|
+
window.location.href = '#{task_path(current_task)}';
|
3
|
+
- else
|
4
|
+
$("#new-comment").replaceWith("#{j render(partial: 'rails_execution/tasks/new_comment', locals: { comment: @new_comment, alert: @alert })}");
|
5
|
+
hljs.highlightAll();
|
6
|
+
window.comments.initAddNewComment('comment_content');
|
@@ -0,0 +1,27 @@
|
|
1
|
+
= re_card_content do
|
2
|
+
%h5.section-title Insights
|
3
|
+
%canvas{ id: :insights_chart, height: 300 }
|
4
|
+
|
5
|
+
:javascript
|
6
|
+
$.get("#{insights_dashboards_path(:json)}", function(data) {
|
7
|
+
new Chart(document.getElementById('insights_chart').getContext('2d'), {
|
8
|
+
type: 'line',
|
9
|
+
data: data,
|
10
|
+
options: {
|
11
|
+
responsive: true,
|
12
|
+
plugins: {
|
13
|
+
legend: false,
|
14
|
+
},
|
15
|
+
layout: {
|
16
|
+
padding: 20,
|
17
|
+
},
|
18
|
+
scales: {
|
19
|
+
y: {
|
20
|
+
min: 0,
|
21
|
+
ticks: { stepSize: 1 },
|
22
|
+
grid: { display: false },
|
23
|
+
},
|
24
|
+
},
|
25
|
+
},
|
26
|
+
});
|
27
|
+
});
|
@@ -0,0 +1,24 @@
|
|
1
|
+
= re_card_content do
|
2
|
+
%h5.section-title Overview
|
3
|
+
.row
|
4
|
+
.col
|
5
|
+
.card.text-center
|
6
|
+
.card-header= link_to 'In Processing', tasks_path
|
7
|
+
.card-body
|
8
|
+
%span.fs-2.fw-lighter= @in_processing_count
|
9
|
+
%span tasks
|
10
|
+
.col
|
11
|
+
.card.text-center
|
12
|
+
.card-header= link_to 'Completed', completed_tasks_path
|
13
|
+
.card-body
|
14
|
+
%span.fs-2.fw-lighter= @tasks_count['completed'].to_i
|
15
|
+
%span tasks
|
16
|
+
.col
|
17
|
+
.card.text-center
|
18
|
+
.card-header= link_to 'Closed', closed_tasks_path
|
19
|
+
.card-body
|
20
|
+
%span.fs-2.fw-lighter= @tasks_count['closed'].to_i
|
21
|
+
%span tasks
|
22
|
+
|
23
|
+
- if show_insights_chart?
|
24
|
+
= render partial: 'rails_execution/dashboards/charts/insights'
|
@@ -0,0 +1,8 @@
|
|
1
|
+
json.labels @tasks_by_week.keys.map { |year, week| Date.commercial(year, week, 1).strftime('Week: %d/%m') }
|
2
|
+
json.datasets [{
|
3
|
+
label: 'Created tasks',
|
4
|
+
data: @tasks_by_week.values,
|
5
|
+
borderWidth: 1,
|
6
|
+
borderColor: 'rgba(255, 99, 132, 1)',
|
7
|
+
backgroundColor: 'rgba(255, 99, 132, 0.2)',
|
8
|
+
}]
|
@@ -0,0 +1,13 @@
|
|
1
|
+
%header.p-3.mb-3.border-bottom.bg-white
|
2
|
+
.container
|
3
|
+
.d-flex.flex-wrap.align-items-center.justify-content-center.justify-content-lg-start
|
4
|
+
= link_to root_path, class: 'd-flex align-items-center mb-2 mb-lg-0 text-dark text-decoration-none' do
|
5
|
+
= image_tag 'executions/logo.png', size: '30x30'
|
6
|
+
%ul.nav.col-12.col-lg-auto.me-lg-auto.mb-2.justify-content-center.mb-md-0.ms-3
|
7
|
+
%li= link_to 'Dashboard', root_path, class: "nav-link px-2 link-#{ current_page?(root_path) ? 'secondary' : 'dark' }"
|
8
|
+
%li= link_to 'Processing', tasks_path, class: "nav-link px-2 link-#{ current_page?(tasks_path) ? 'secondary' : 'dark' }"
|
9
|
+
%li= link_to 'Completed', completed_tasks_path, class: "nav-link px-2 link-#{ current_page?(completed_tasks_path) ? 'secondary' : 'dark' }"
|
10
|
+
%li= link_to 'Closed', closed_tasks_path, class: "nav-link px-2 link-#{ current_page?(closed_tasks_path) ? 'secondary' : 'dark' }"
|
11
|
+
.col-md-3.text-end
|
12
|
+
= yield :page_actions
|
13
|
+
= link_to 'New Task', new_task_path, class: 'btn btn-outline-primary ms-2', disabled: !can_create_task?
|
@@ -0,0 +1,35 @@
|
|
1
|
+
- return if total_pages <= 1
|
2
|
+
- original_params = params.permit!.to_h
|
3
|
+
|
4
|
+
- gap_number = 2
|
5
|
+
- page_numbers = (1..total_pages).to_a
|
6
|
+
- begin_paging = (1..(1 + gap_number)).to_a
|
7
|
+
- middle_paging = page_numbers & ((page - gap_number)..(page + gap_number)).to_a
|
8
|
+
- end_paging = page_numbers & ((total_pages - gap_number)..total_pages).to_a
|
9
|
+
|
10
|
+
- if begin_paging.last < middle_paging.first.pred
|
11
|
+
- begin_paging[-1] = 'gap1'
|
12
|
+
- if middle_paging.last < end_paging.first.pred
|
13
|
+
- end_paging[0] = 'gap2'
|
14
|
+
- page_numbers = begin_paging + middle_paging + end_paging
|
15
|
+
|
16
|
+
%nav.execution-paging
|
17
|
+
%ul.pagination.justify-content-center
|
18
|
+
- if page > 1
|
19
|
+
%li.page-item
|
20
|
+
= link_to original_params.merge(page: page.pred), class: 'page-link' do
|
21
|
+
%i.bi.bi-chevron-left
|
22
|
+
|
23
|
+
- page_numbers.uniq.each do |page_num|
|
24
|
+
- if page == page_num
|
25
|
+
- item_css = 'active'
|
26
|
+
- elsif page_num.to_s.start_with?('gap')
|
27
|
+
- page_num, item_css = '...', 'disabled'
|
28
|
+
|
29
|
+
%li.page-item{ class: item_css }
|
30
|
+
= link_to page_num, original_params.merge(page: page_num), class: 'page-link'
|
31
|
+
|
32
|
+
- if page < total_pages
|
33
|
+
%li.page-item
|
34
|
+
= link_to original_params.merge(page: page.next), class: 'page-link' do
|
35
|
+
%i.bi.bi-chevron-right
|
@@ -0,0 +1,12 @@
|
|
1
|
+
- unless current_task.is_completed?
|
2
|
+
= re_card_content do
|
3
|
+
.text-center
|
4
|
+
- if can_execute_task?(current_task)
|
5
|
+
= link_to 'Execute NOW!!!', execute_task_path(current_task), class: 'btn btn-primary', method: :patch, data: { confirm: 'Are you sure?' }
|
6
|
+
- else
|
7
|
+
%p.text-danger.mb-0.fw-bold
|
8
|
+
%i.bi.bi-x-octagon.me-1
|
9
|
+
Not ready to Execute!
|
10
|
+
%span.fs-13.text-muted.fw-lighter.mb-0
|
11
|
+
%i.bi.bi-megaphone
|
12
|
+
= how_to_executable(current_task)
|
@@ -0,0 +1,22 @@
|
|
1
|
+
- if task_logs.any?
|
2
|
+
= re_card_content do
|
3
|
+
%h5.section-title Logs
|
4
|
+
%ul.list-unstyled#task-logs
|
5
|
+
- task_logs.each do |task_log|
|
6
|
+
%li.fs-13.row.mb-3
|
7
|
+
.col.text-muted
|
8
|
+
%i.bi.bi-journal-text
|
9
|
+
= re_get_file_name(task_log)
|
10
|
+
.col-3.text-end= link_to 'Download', task_log, target: :_blank, class: 'text-muted click2download'
|
11
|
+
|
12
|
+
= re_card_content id: 'activities-section' do
|
13
|
+
%h5.section-title Activities
|
14
|
+
%ul.list-unstyled
|
15
|
+
- current_task.activities.descending.preload(:owner).each do |activitiy|
|
16
|
+
%li.row
|
17
|
+
.col
|
18
|
+
= render_user_info(activitiy.owner, avatar_size: '20x20')
|
19
|
+
%p.normal-text.mt-1= activitiy.message
|
20
|
+
.col-3.text-end.activitiy-time
|
21
|
+
%span.fs13px.text-muted= activitiy.created_at.strftime('%Y/%m/%d')
|
22
|
+
%p.text-muted= activitiy.created_at.strftime('%H:%M')
|
@@ -0,0 +1,3 @@
|
|
1
|
+
%li.row.mb-2
|
2
|
+
.col= text_field :attachments, :name, { class: 'form-control file-name', placeholder: 'Name', name: "attachments[0][name]" }
|
3
|
+
.col-6.overflow-hidden= file_field :attachments, :file, { class: 'form-control file-upload', accept: re_attachment_file_acceptable_types, name: "attachments[0][file]" }
|
@@ -0,0 +1,12 @@
|
|
1
|
+
- if RailsExecution.configuration.file_upload && task_attachment_files.any?
|
2
|
+
= re_card_content do
|
3
|
+
%h5.section-title.mb-3 Attachment files
|
4
|
+
%ul.list-unstyled.mt-3
|
5
|
+
- task_attachment_files.each do |name, url|
|
6
|
+
%li.row.normal-text
|
7
|
+
.col
|
8
|
+
%i.bi.fs-3.text-muted{ class: "bi-file-text bi-filetype-#{url.split('.').last}" }
|
9
|
+
= name
|
10
|
+
.col-3.text-end
|
11
|
+
%p.mb-0= link_to 'Download', url, class: 'text-muted'
|
12
|
+
%small Filename: #{re_get_file_name(url)}
|
@@ -0,0 +1,15 @@
|
|
1
|
+
= render partial: 'new_comment', locals: { comment: (new_comment || current_task.comments.new) }
|
2
|
+
= render current_task.comments.ascending
|
3
|
+
|
4
|
+
:javascript
|
5
|
+
hljs.highlightAll();
|
6
|
+
window.comments.initAddNewComment('comment_content');
|
7
|
+
|
8
|
+
$(document).on('click', '.media-comment a.edit-comment', function() {
|
9
|
+
window.comments.clickEditComment(this);
|
10
|
+
return false;
|
11
|
+
});
|
12
|
+
$(document).on('click', '.media-comment a.cancel-edit-mode', function() {
|
13
|
+
window.comments.cancelEditMode(this);
|
14
|
+
return false;
|
15
|
+
});
|
@@ -0,0 +1,55 @@
|
|
1
|
+
= form_for task, html: { enctype: 'multipart/form-data' } do |f|
|
2
|
+
.row
|
3
|
+
- if task.errors.any?
|
4
|
+
.col-12.text-danger.small.mt-3= task.errors.full_messages.join("\n")
|
5
|
+
|
6
|
+
.col
|
7
|
+
.mb-3.mt-4= f.text_field :title, { class: 'form-control', placeholder: 'Title', required: true }
|
8
|
+
.mb-3.mt-4= f.text_area :description, { class: 'form-control', placeholder: 'Description', rows: 10 }
|
9
|
+
- if show_form_sidebar?(task)
|
10
|
+
.col-4
|
11
|
+
- unless in_solo_mode?
|
12
|
+
%h5.mb-2.mt-4.section-title Reviewers
|
13
|
+
#reviewers.mb-3
|
14
|
+
- if reviewers.any?
|
15
|
+
= f.collection_select :task_review_ids, reviewers, :id, :name, { selected: task.reviewer_ids }, { multiple: true, class: 'select2 col-12' }
|
16
|
+
- else
|
17
|
+
%p.small.text-muted Nobody can add
|
18
|
+
|
19
|
+
- if RailsExecution.configuration.file_upload
|
20
|
+
%h5.mb-2.mt-4.section-title Attachment files
|
21
|
+
%ul.list-unstyled.mt-3
|
22
|
+
- task_attachment_files.each do |name, url|
|
23
|
+
%li.row.normal-text
|
24
|
+
.col
|
25
|
+
%i.bi.fs-3.text-muted{ class: "bi-file-text bi-filetype-#{url.split('.').last}" }
|
26
|
+
= name
|
27
|
+
.col-6.text-end
|
28
|
+
%p.mb-0= link_to 'Download', url, class: 'text-muted'
|
29
|
+
%small Filename: #{re_get_file_name(url)}
|
30
|
+
#attachment_files.mb-3
|
31
|
+
%ul.list-unstyled= render partial: 'attachment_file_fields'
|
32
|
+
|
33
|
+
.mt-3.text-center
|
34
|
+
%hr.smoothly
|
35
|
+
= link_to 'More file', '#add-more-file', data: { fields_html: render(partial: 'attachment_file_fields') }, class: 'btn text-primary', id: 'js-add-more-file'
|
36
|
+
|
37
|
+
|
38
|
+
- if task.new_record? || task.in_processing?
|
39
|
+
.col-md-12.mb-3.mt-3
|
40
|
+
.row
|
41
|
+
.col
|
42
|
+
%h5.section-title Script
|
43
|
+
.col.text-end= render partial: 'tips'
|
44
|
+
= f.text_area :script, { class: 'form-control hidden', rows: 3, id: 'main-script' }
|
45
|
+
#script-editor-container
|
46
|
+
#script-editor= task.script
|
47
|
+
= link_to '#full-screen', class: 'btn', id: 'click2full' do
|
48
|
+
%i.bi.bi-arrows-fullscreen
|
49
|
+
%i.bi.bi-arrows-angle-contract
|
50
|
+
|
51
|
+
.mb-3.mt-3.text-end
|
52
|
+
%hr
|
53
|
+
= f.submit (task.new_record? ? 'Create' : 'Update'), class: 'btn btn-success'
|
54
|
+
|
55
|
+
= render partial: 'form_scripts', locals: { task: task }
|
@@ -0,0 +1,34 @@
|
|
1
|
+
:javascript
|
2
|
+
$('#task_task_review_ids').select2({});
|
3
|
+
new EasyMDE({
|
4
|
+
element: document.getElementById('task_description'),
|
5
|
+
status: false,
|
6
|
+
showIcons: ['code'],
|
7
|
+
placeholder: "Type description here...",
|
8
|
+
renderingConfig: {
|
9
|
+
codeSyntaxHighlighting: true,
|
10
|
+
}
|
11
|
+
});
|
12
|
+
|
13
|
+
- if task.new_record? || task.in_processing?
|
14
|
+
:javascript
|
15
|
+
var editor = ace.edit("script-editor", { printMarginColumn: false });
|
16
|
+
var RubyMode = ace.require("ace/mode/ruby").Mode;
|
17
|
+
editor.setTheme("ace/theme/solarized_dark");
|
18
|
+
editor.session.setMode(new RubyMode());
|
19
|
+
editor.renderer.setPadding(15);
|
20
|
+
editor.renderer.setShowPrintMargin(false);
|
21
|
+
editor.getSession().on("change", function () {
|
22
|
+
$("#main-script").val(editor.getValue());
|
23
|
+
});
|
24
|
+
|
25
|
+
$('#click2full').click(function() {
|
26
|
+
$('#script-editor-container').toggleClass('fullscreen');
|
27
|
+
return false;
|
28
|
+
});
|
29
|
+
|
30
|
+
$('#js-add-more-file').click(function() {
|
31
|
+
AddMoreFile();
|
32
|
+
return false;
|
33
|
+
});
|
34
|
+
|
@@ -0,0 +1,8 @@
|
|
1
|
+
#new-comment
|
2
|
+
= re_card_content do
|
3
|
+
= form_for comment || current_task.comments.new, url: task_comments_path(current_task), remote: true do |f|
|
4
|
+
= f.text_area :content, { rows: 3 }
|
5
|
+
.mt-3
|
6
|
+
.text-start.text-danger= alert
|
7
|
+
.text-end= f.submit 'Comment', class: 'btn btn-outline-success'
|
8
|
+
|
@@ -0,0 +1,23 @@
|
|
1
|
+
- if display_reviewers?(current_task)
|
2
|
+
= re_card_content do
|
3
|
+
%h5.section-title Reviewers
|
4
|
+
%ul.list-unstyled.mt-3
|
5
|
+
- if reviewing_accounts.empty?
|
6
|
+
%li.mt-2.small.text-muted.text-center Nobody!
|
7
|
+
- else
|
8
|
+
- reviewing_accounts.each do |task_review|
|
9
|
+
%li.mt-2
|
10
|
+
.row
|
11
|
+
.col= render_user_info(task_review.owner, avatar_size: '20x20')
|
12
|
+
.col-3.text-end= re_badge_color(task_review.status)
|
13
|
+
|
14
|
+
- if display_decide?(current_task)
|
15
|
+
= re_card_content do
|
16
|
+
%h5.section-title What is your decision?
|
17
|
+
.row.mt-3
|
18
|
+
.col= render_user_info(current_owner, avatar_size: '20x20')
|
19
|
+
.col-6.text-end
|
20
|
+
- unless task_reviewed_status(current_task)&.rejected?
|
21
|
+
= link_to 'Reject', reject_task_path(current_task), method: :patch, data: { confirm: 'Are you sure?' }, class: 'btn btn-sm btn-danger'
|
22
|
+
- unless task_reviewed_status(current_task)&.approved?
|
23
|
+
= link_to 'Approve', approve_task_path(current_task), method: :patch, data: { confirm: 'Are you sure?' }, class: 'btn btn-sm btn-success'
|
@@ -0,0 +1,15 @@
|
|
1
|
+
= re_card_content do
|
2
|
+
- if current_task.script.blank?
|
3
|
+
%p.text-center.text-muted.mt-3
|
4
|
+
%i.bi.bi-quote.fs-5
|
5
|
+
Script is empty!
|
6
|
+
- else
|
7
|
+
%h5.section-title.mb-3 Script
|
8
|
+
#task-detail
|
9
|
+
= link_to '#full-screen', class: 'btn', id: 'click2full' do
|
10
|
+
%i.bi.bi-arrows-fullscreen
|
11
|
+
%i.bi.bi-arrows-angle-contract
|
12
|
+
#script-editor.collapsed= current_task.script
|
13
|
+
= link_to '#detail', class: 'btn', id: 'click2detail' do
|
14
|
+
%i.bi.bi-arrow-bar-down.me-2
|
15
|
+
Click to detail
|
@@ -0,0 +1,27 @@
|
|
1
|
+
- if current_task.script.present?
|
2
|
+
:javascript
|
3
|
+
window.editor = ace.edit("script-editor", {
|
4
|
+
wrap: true,
|
5
|
+
readOnly: true,
|
6
|
+
printMarginColumn: false,
|
7
|
+
});
|
8
|
+
var RubyMode = ace.require("ace/mode/ruby").Mode;
|
9
|
+
editor.setTheme("ace/theme/solarized_dark");
|
10
|
+
editor.session.setMode(new RubyMode());
|
11
|
+
|
12
|
+
$('#click2detail').click(function() {
|
13
|
+
this.remove();
|
14
|
+
$('#script-editor').removeClass('collapsed');
|
15
|
+
window.editor.resize();
|
16
|
+
return false;
|
17
|
+
})
|
18
|
+
$('#click2full').click(function() {
|
19
|
+
$('#task-detail').toggleClass('fullscreen');
|
20
|
+
window.editor.resize();
|
21
|
+
return false;
|
22
|
+
})
|
23
|
+
|
24
|
+
- if current_task.description.present?
|
25
|
+
:javascript
|
26
|
+
$('#description-html').html(marked.parse($('#description-markdown').html()));
|
27
|
+
hljs.highlightAll();
|
@@ -0,0 +1,19 @@
|
|
1
|
+
= re_card_content do
|
2
|
+
%h5.section-title All checks
|
3
|
+
%ul.list-unstyled.mt-3
|
4
|
+
%li.mt-2.row
|
5
|
+
.col
|
6
|
+
.user-info
|
7
|
+
= image_tag 'executions/robot.png', size: '20x20', class: 'bd-placeholder-img flex-shrink-0 me-2 rounded'
|
8
|
+
%span Task status
|
9
|
+
.col-3.text-end= re_badge_color(current_task.status)
|
10
|
+
%li.mt-2.row
|
11
|
+
.col
|
12
|
+
.user-info
|
13
|
+
= image_tag 'executions/robot.png', size: '20x20', class: 'bd-placeholder-img flex-shrink-0 me-2 rounded'
|
14
|
+
%span Syntax
|
15
|
+
.col-3.text-end
|
16
|
+
- if current_task.syntax_status_good?
|
17
|
+
%i.fs-5.bi.bi-check-lg.text-success
|
18
|
+
- else
|
19
|
+
%i.fs-5.bi.bi-exclamation-triangle.text-danger
|
@@ -0,0 +1,18 @@
|
|
1
|
+
%li.border-bottom.d-flex.pt-2.pb-2{ class: "task-is-#{task.status}" }
|
2
|
+
.p-2.pt-3
|
3
|
+
%span.fs-4.text-muted ##{task.id}
|
4
|
+
.p-2
|
5
|
+
= link_to task_path(task) do
|
6
|
+
= render_owner_avatar(task.owner, size: '50x50')
|
7
|
+
.p-2
|
8
|
+
= link_to task_path(task) do
|
9
|
+
%p.mb-0.text-black
|
10
|
+
= task.title.truncate(100)
|
11
|
+
- if task.syntax_status_good?
|
12
|
+
%i.bi.bi-check2.text-success
|
13
|
+
- elsif task.syntax_status_bad?
|
14
|
+
%i.bi.bi-exclamation-octagon.text-danger
|
15
|
+
%span.small.text-muted created on #{time_ago_in_words(task.created_at)} by #{render_owner_name(task.owner)}
|
16
|
+
.p-2.ms-auto.text-end
|
17
|
+
%span.me-1= task.comments.size
|
18
|
+
%i.bi.bi-chat-left-dots
|