rails_execution 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. checksums.yaml +7 -0
  2. data/.ruby-version +1 -0
  3. data/Gemfile +8 -0
  4. data/Gemfile.lock +129 -0
  5. data/LICENSE.txt +21 -0
  6. data/README.md +35 -0
  7. data/Rakefile +3 -0
  8. data/app/assets/images/executions/approved.png +0 -0
  9. data/app/assets/images/executions/favicon.png +0 -0
  10. data/app/assets/images/executions/logo.png +0 -0
  11. data/app/assets/images/executions/rejected.png +0 -0
  12. data/app/assets/images/executions/robot.png +0 -0
  13. data/app/assets/javascripts/executions/ace.min.js +1 -0
  14. data/app/assets/javascripts/executions/ace.modes.ruby.min.js +1 -0
  15. data/app/assets/javascripts/executions/ace.theme-solarized_dark.min.js +1 -0
  16. data/app/assets/javascripts/executions/base.js +1 -0
  17. data/app/assets/javascripts/executions/bootstrap.5.2.1.min.js +6 -0
  18. data/app/assets/javascripts/executions/chart.min.js +13 -0
  19. data/app/assets/javascripts/executions/comments.js +43 -0
  20. data/app/assets/javascripts/executions/easymde.min.js +7 -0
  21. data/app/assets/javascripts/executions/events.js +8 -0
  22. data/app/assets/javascripts/executions/highlight.min.js +1198 -0
  23. data/app/assets/javascripts/executions/highlight.ruby.min.js +51 -0
  24. data/app/assets/javascripts/executions/jquery-3.6.1.min.js +2 -0
  25. data/app/assets/javascripts/executions/marked.min.js +6 -0
  26. data/app/assets/javascripts/executions/rails.js +565 -0
  27. data/app/assets/javascripts/executions/select2.min.js +2 -0
  28. data/app/assets/stylesheets/executions/base.css +3 -0
  29. data/app/assets/stylesheets/executions/bootstrap-icons.css +1869 -0
  30. data/app/assets/stylesheets/executions/comments.css +14 -0
  31. data/app/assets/stylesheets/executions/easymde.min.css +7 -0
  32. data/app/assets/stylesheets/executions/fonts.css +4 -0
  33. data/app/assets/stylesheets/executions/highlight.min.css +9 -0
  34. data/app/assets/stylesheets/executions/modify.scss +278 -0
  35. data/app/assets/stylesheets/executions/select2.min.css +1 -0
  36. data/app/controllers/rails_execution/base_controller.rb +16 -0
  37. data/app/controllers/rails_execution/comments_controller.rb +34 -0
  38. data/app/controllers/rails_execution/dashboards_controller.rb +27 -0
  39. data/app/controllers/rails_execution/tasks_controller.rb +175 -0
  40. data/app/helpers/rails_execution/base_helper.rb +14 -0
  41. data/app/helpers/rails_execution/policy_helper.rb +64 -0
  42. data/app/helpers/rails_execution/rendering_helper.rb +94 -0
  43. data/app/models/rails_execution/activity.rb +8 -0
  44. data/app/models/rails_execution/comment.rb +8 -0
  45. data/app/models/rails_execution/task.rb +71 -0
  46. data/app/models/rails_execution/task_review.rb +14 -0
  47. data/app/views/layouts/execution.html.haml +16 -0
  48. data/app/views/rails_execution/comments/_comment.html.haml +23 -0
  49. data/app/views/rails_execution/comments/create.js.haml +6 -0
  50. data/app/views/rails_execution/comments/update.js.haml +2 -0
  51. data/app/views/rails_execution/dashboards/charts/_insights.html.haml +27 -0
  52. data/app/views/rails_execution/dashboards/home.html.haml +24 -0
  53. data/app/views/rails_execution/dashboards/insights.json.jbuilder +8 -0
  54. data/app/views/rails_execution/shared/_flash.html.haml +7 -0
  55. data/app/views/rails_execution/shared/_header.html.haml +13 -0
  56. data/app/views/rails_execution/shared/_paging.html.haml +35 -0
  57. data/app/views/rails_execution/tasks/_actions.html.haml +12 -0
  58. data/app/views/rails_execution/tasks/_activities.html.haml +22 -0
  59. data/app/views/rails_execution/tasks/_attachment_file_fields.html.haml +3 -0
  60. data/app/views/rails_execution/tasks/_attachment_files.html.haml +12 -0
  61. data/app/views/rails_execution/tasks/_comments.html.haml +15 -0
  62. data/app/views/rails_execution/tasks/_form.html.haml +55 -0
  63. data/app/views/rails_execution/tasks/_form_scripts.html.haml +34 -0
  64. data/app/views/rails_execution/tasks/_new_comment.html.haml +8 -0
  65. data/app/views/rails_execution/tasks/_reviewers.html.haml +23 -0
  66. data/app/views/rails_execution/tasks/_script_content.html.haml +15 -0
  67. data/app/views/rails_execution/tasks/_show_scripts.html.haml +27 -0
  68. data/app/views/rails_execution/tasks/_status.html.haml +19 -0
  69. data/app/views/rails_execution/tasks/_task.html.haml +18 -0
  70. data/app/views/rails_execution/tasks/_tips.html.haml +18 -0
  71. data/app/views/rails_execution/tasks/closed.html.haml +12 -0
  72. data/app/views/rails_execution/tasks/completed.html.haml +12 -0
  73. data/app/views/rails_execution/tasks/edit.html.haml +3 -0
  74. data/app/views/rails_execution/tasks/index.html.haml +12 -0
  75. data/app/views/rails_execution/tasks/new.html.haml +3 -0
  76. data/app/views/rails_execution/tasks/show.html.haml +38 -0
  77. data/config/routes.rb +26 -0
  78. data/lib/generators/rails_execution/file_upload_generator.rb +18 -0
  79. data/lib/generators/rails_execution/install_generator.rb +36 -0
  80. data/lib/generators/rails_execution/templates/config.rb.tt +63 -0
  81. data/lib/generators/rails_execution/templates/file_reader.rb.tt +14 -0
  82. data/lib/generators/rails_execution/templates/file_uploader.rb.tt +11 -0
  83. data/lib/generators/rails_execution/templates/install.rb.tt +48 -0
  84. data/lib/rails_execution/app_model.rb +14 -0
  85. data/lib/rails_execution/config.rb +73 -0
  86. data/lib/rails_execution/engine.rb +24 -0
  87. data/lib/rails_execution/error.rb +6 -0
  88. data/lib/rails_execution/files/reader.rb +49 -0
  89. data/lib/rails_execution/files/uploader.rb +42 -0
  90. data/lib/rails_execution/services/approvement.rb +41 -0
  91. data/lib/rails_execution/services/execution.rb +87 -0
  92. data/lib/rails_execution/services/executor.rb +24 -0
  93. data/lib/rails_execution/services/paging.rb +35 -0
  94. data/lib/rails_execution/services/syntax_checker.rb +38 -0
  95. data/lib/rails_execution/version.rb +5 -0
  96. data/lib/rails_execution.rb +24 -0
  97. 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,8 @@
1
+ class RailsExecution::Activity < RailsExecution::AppModel
2
+
3
+ belongs_to :task
4
+
5
+ validates :task, presence: true
6
+ validates :message, presence: true
7
+
8
+ end
@@ -0,0 +1,8 @@
1
+ class RailsExecution::Comment < RailsExecution::AppModel
2
+
3
+ belongs_to :task
4
+
5
+ validates :task, presence: true
6
+ validates :content, presence: true
7
+
8
+ 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,2 @@
1
+ $("##{params[:comment_key]}").replaceWith("#{j render(partial: 'comment', locals: { comment: @comment })}");
2
+ hljs.highlightAll();
@@ -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,7 @@
1
+ #flash-messages
2
+ - flash.each do |key, message|
3
+ = render_notification_message(key, message)
4
+ :javascript
5
+ setTimeout(function() {
6
+ $('#flash-messages .alert').slideUp();
7
+ }, 3000);
@@ -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