diff --git a/.rubocop.yml b/.rubocop.yml index ddbc53a..2f5dff1 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -5,6 +5,9 @@ AllCops: - bin/**/* - vendor/assets/**/* +Lint/Debugger: + AutoCorrect: False + Style/AndOr: Enabled: false diff --git a/.sass-lint.yml b/.sass-lint.yml new file mode 100644 index 0000000..84ba9b0 --- /dev/null +++ b/.sass-lint.yml @@ -0,0 +1,28 @@ +# https://github.com/sasstools/sass-lint/tree/master/docs/rules +files: + include: app/assets/stylesheets/**/*.scss + +options: + formatter: stylish + merge-default-rules: true + +rules: + class-name-format: 0 + id-name-format: 0 + leading-zero: + - 1 + - include: true + no-duplicate-properties: + - 1 + - + exclude: + - src # for @font mixins + no-qualifying-elements: + - 1 + - allow-element-with-attribute: true # input[type='email'] but not div.class-name + quotes: 0 + no-vendor-prefixes: + - + excluded-identifiers: + - -moz-osx-font-smoothing + - -webkit-font-smoothing diff --git a/Guardfile b/Guardfile index 1027353..305fd8e 100644 --- a/Guardfile +++ b/Guardfile @@ -79,7 +79,7 @@ guard :shell, all_on_start: true do end end -guard :rubocop, cli: %w(-D -S) do +guard :rubocop, cli: %w(-D -S -a) do watch(/.rubocop.yml/) watch(/.+\.rb$/) watch(/Rakefile/) diff --git a/app/assets/stylesheets/molecules/_admin_review.scss b/app/assets/stylesheets/molecules/_admin_review.scss index 586367d..9077423 100644 --- a/app/assets/stylesheets/molecules/_admin_review.scss +++ b/app/assets/stylesheets/molecules/_admin_review.scss @@ -1,12 +1,14 @@ .admin-review { counter-reset: question; + float: left; + width: 66%; form { margin-left: 2.3em; position: relative; &::before { - content: counter(question) ") "; + content: counter(question) ') '; counter-increment: question; font-size: 1.25em; left: -1.8em; @@ -17,6 +19,59 @@ } +.review-comments { + float: right; + padding-left: 30px; + width: 33%; + + .comment-message { + margin-right: 5px; + } + + .comment-author { + font-size: 0.85em; + font-weight: 700; + margin-bottom: 30px; + text-align: right; + + &::before { + content: '- '; + } + } + + .comment-edit-stamp { + color: rgba($gray-dark, 0.65); + font-size: 0.75em; + text-align: right; + } + + .comment-edit-btn { + cursor: pointer; + display: inline-block; + font-size: 0.85em; + font-weight: bold; + padding: 2px 5px; + + &:hover { + background-color: $gray-base; + color: $gray-lighter; + } + } + + .comment-edit-form { + display: none; + margin: 30px 0; + padding: 10px 0; + + } + + [type="checkbox"] { + &:checked + .comment-edit-form { + display: block; + } + } +} + .review_meta { @media screen and (min-width: 768px) { diff --git a/app/controllers/admin/comment_controller.rb b/app/controllers/admin/comment_controller.rb new file mode 100644 index 0000000..6da6843 --- /dev/null +++ b/app/controllers/admin/comment_controller.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true +module Admin + class CommentController < AdminController + def update + comment = QuizComment.find_by(id: params[:id], test_hash: params[:test_hash]) + authorize comment + + comment.update(comment_params) + flash_message = if comment.save + { success: "Sucessfully updated comment" } + else + { error: "Failed to update comment" } + end + redirect_to admin_result_path(params[:test_hash]), flash: flash_message + end + + def create + comment = QuizComment.new(comment_params.merge(user_id: current_user.id, test_hash: params[:test_hash])) + authorize comment + + flash_message = if comment.save + { success: "Sucessfully created comment" } + else + { error: "Failed to save comment" } + end + + ReviewerMailer.new_comment(comment).deliver_later if comment.persisted? + redirect_to admin_result_path(params[:test_hash]), flash: flash_message + end + + private + + def comment_params + params.require(:quiz_comment).permit(:message) + end + end +end diff --git a/app/controllers/admin/result_controller.rb b/app/controllers/admin/result_controller.rb index 867c922..67be27a 100644 --- a/app/controllers/admin/result_controller.rb +++ b/app/controllers/admin/result_controller.rb @@ -19,6 +19,8 @@ module Admin @candidate = Candidate.find_by(test_hash: params[:test_hash]) @quiz = @candidate.my_quiz @status = QuizStatus.new(@candidate) + @comments = QuizComment.includes(:user).where(test_hash: @candidate.test_hash).order(:created_at) + @comment = QuizComment.new end end end diff --git a/app/mailers/reviewer_mailer.rb b/app/mailers/reviewer_mailer.rb index 3955ef9..b640169 100644 --- a/app/mailers/reviewer_mailer.rb +++ b/app/mailers/reviewer_mailer.rb @@ -19,4 +19,11 @@ class ReviewerMailer < ApplicationMailer mail to: @manager.email, subject: "Voting Complete" end + + def new_comment comment + @comment = comment + recipients = comment.candidate.reviewers.map(&:email) + + mail to: recipients, subject: "Skills Assessment Review Comment - #{@comment.test_hash}" + end end diff --git a/app/models/candidate.rb b/app/models/candidate.rb index 7d5b287..af91ff7 100644 --- a/app/models/candidate.rb +++ b/app/models/candidate.rb @@ -6,6 +6,7 @@ class Candidate < ApplicationRecord belongs_to :recruiter, class_name: "User" has_many :votes, class_name: "ReviewerVote" has_many :reviewers, through: :quiz + has_many :quiz_comments, foreign_key: :test_hash, primary_key: :test_hash serialize :email, CryptSerializer diff --git a/app/models/quiz_comment.rb b/app/models/quiz_comment.rb new file mode 100644 index 0000000..a918447 --- /dev/null +++ b/app/models/quiz_comment.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true +class QuizComment < ApplicationRecord + belongs_to :user + belongs_to :candidate, foreign_key: :test_hash, primary_key: :test_hash + + validates :message, presence: true + + def edits? + updated_at > (created_at + 5.seconds) + end +end diff --git a/app/models/user.rb b/app/models/user.rb index 773cb1f..6179a94 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -5,6 +5,7 @@ class User < ApplicationRecord has_many :reviewer_to_quizzes has_many :quizzes, through: :reviewer_to_quizzes has_many :votes, class_name: 'ReviewerVote' + has_many :quiz_comments has_many :reviewees, through: :quizzes, source: :candidates diff --git a/app/policies/quiz_comment_policy.rb b/app/policies/quiz_comment_policy.rb new file mode 100644 index 0000000..f4128f9 --- /dev/null +++ b/app/policies/quiz_comment_policy.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true +class QuizCommentPolicy < ApplicationPolicy + # Quiz Comment Policy + # + # Anyone who can vote on results, can comment + # Only comment owner can edit her comment + + def new? + user.acts_as_reviewer? + end + + def create? + user.acts_as_reviewer? && record.candidate.reviewers.where(id: user.id).count.positive? + end + + def update? + user.acts_as_reviewer? && user.id == record.user_id + end +end diff --git a/app/views/admin/result/_comment.html.erb b/app/views/admin/result/_comment.html.erb new file mode 100644 index 0000000..d47d8be --- /dev/null +++ b/app/views/admin/result/_comment.html.erb @@ -0,0 +1,21 @@ +
+ <%= comment.message %> + + <% if policy(comment).update? %> + + <% end %> + + <% if comment.edits? %> +
Updated <%= time_ago_in_words(comment.updated_at) %> ago
+ <% end %> +
+ +
<%= comment.user.name %>
+ + +<% if policy(comment).update? %> + +
+ <%= render partial: 'comment_form', locals: {comment: comment, test_hash: comment.test_hash } %> +
+<% end %> diff --git a/app/views/admin/result/_comment_form.html.erb b/app/views/admin/result/_comment_form.html.erb new file mode 100644 index 0000000..701cb7a --- /dev/null +++ b/app/views/admin/result/_comment_form.html.erb @@ -0,0 +1,25 @@ +<%= render partial: 'shared/form_model_errors', locals: { obj: comment } %> + +<% if comment.id.nil? %> + + <%= form_for comment, url: admin_create_comment_path(test_hash: test_hash), method: :post do |form| %> +
+ <%= form.label :message, "New Comment" %> + <%= form.text_area :message %> +
+ + <%= submit_tag "Save Comment" %> + <% end %> + +<% else %> + + <%= form_for comment, url: admin_update_comment_path(test_hash: test_hash, id: comment.id), method: :post do |form| %> +
+ <%= form.label :message, "Update Comment" %> + <%= form.text_area :message %> +
+ + <%= submit_tag "Update" %> + <% end %> + +<% end %> diff --git a/app/views/admin/result/view.html.erb b/app/views/admin/result/view.html.erb index d0452ca..0372f3d 100644 --- a/app/views/admin/result/view.html.erb +++ b/app/views/admin/result/view.html.erb @@ -2,42 +2,52 @@ content_for :title, "Quiz Review - Skills Assessment Admin" %> -
-

Quiz Review

+
+
+

Quiz Review

-
-
- Test ID: <%= @candidate.test_hash %>
- Years of Experience: <%= @candidate.experience %>
- Client/Project: <%= @candidate.project %>
- Recruiter Email: <%= mail_to @candidate.recruiter.name, @candidate.recruiter.email %>
+
+
+ Test ID: <%= @candidate.test_hash %>
+ Years of Experience: <%= @candidate.experience %>
+ Client/Project: <%= @candidate.project %>
+ Recruiter Email: <%= mail_to @candidate.recruiter.name, @candidate.recruiter.email %>
+
+ +
<%= render partial: 'voting' %>
-
<%= render partial: 'voting' %>
+ <% @quiz.each do |question| %> + <%= form_for(:answer, url: '#never-post', html:{id: 'summary-form'}) do |form| %> +
+
+
+

<%= question.question %>

+
+
+ +
+ <% if question.attachment.present? %> + <%= image_tag question.attachment %> + <% end %> +
+ <%= render partial: "quiz/#{question.input_type}", locals: {question: question, answer: question.answer, form: form} %> +
+
+
+ <% end #form_tag %> + <% end #questions loop %> + + <%= link_to(admin_results_path, { class: 'secondary-btn' }) do %> + + <% end %>
- <% @quiz.each do |question| %> - <%= form_for(:answer, url: '#never-post', html:{id: 'summary-form'}) do |form| %> -
-
-
-

<%= question.question %>

-
-
- -
- <% if question.attachment.present? %> - <%= image_tag question.attachment %> - <% end %> -
- <%= render partial: "quiz/#{question.input_type}", locals: {question: question, answer: question.answer, form: form} %> -
-
-
- <% end #form_tag %> - <% end #questions loop %> - - <%= link_to(admin_results_path, { class: 'secondary-btn' }) do %> - - <% end %> -
+
+

Comments

+ <%= render partial: 'comment', collection: @comments, locals: { test_hash: @candidate.test_hash } %> + <% if policy(QuizComment).new? %> + <%= render partial: 'comment_form', locals: {comment: @comment, test_hash: @candidate.test_hash } %> + <% end %> +
+ diff --git a/app/views/layouts/mailer.html.inky b/app/views/layouts/mailer.html.inky index 12f03c3..9761da3 100644 --- a/app/views/layouts/mailer.html.inky +++ b/app/views/layouts/mailer.html.inky @@ -23,7 +23,7 @@