Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make evaluation creation a full stepper #3080

Merged
merged 19 commits into from
Sep 10, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 65 additions & 21 deletions app/assets/javascripts/evaluation.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,5 @@
import { fetch } from "util.js";

export function interceptAddMultiUserClicks(): void {
let running = false;
document.querySelectorAll(".user-select-option a").forEach(option => {
option.addEventListener("click", async event => {
if (!running) {
running = true;
event.preventDefault();
const button = option.querySelector(".button");
const loader = option.querySelector(".loader");
button.classList.add("hidden");
loader.classList.remove("hidden");
const response = await fetch(option.getAttribute("href"), { method: "POST" });
eval(await response.text());
loader.classList.add("hidden");
button.classList.remove("hidden");
running = false;
}
});
});
}

export function initCheckboxes(): void {
document.querySelectorAll(".evaluation-users-table .user-row").forEach(el => initCheckbox(el));
}
Expand All @@ -39,3 +18,68 @@ export function initCheckbox(row: HTMLTableRowElement): void {
}
});
}

export function initEvaluationStepper(): void {
const evalPanelElement = document.querySelector("#info-panel .panel-collapse");
const evalPanel = new bootstrap.Collapse(evalPanelElement, { toggle: false });
const userPanelElement = document.querySelector("#users-panel .panel-collapse");
const userPanel = new bootstrap.Collapse(userPanelElement, { toggle: false });
const scorePanelElement = document.querySelector("#items-panel .panel-collapse");
const scorePanel = new bootstrap.Collapse(scorePanelElement, { toggle: false });

function init(): void {
window.dodona.toUsersStep = toUsersStep;

evalPanelElement.addEventListener("show.bs.collapse", function () {
userPanel.hide();
scorePanel.hide();
});
userPanelElement.addEventListener("show.bs.collapse", function () {
evalPanel.hide();
scorePanel.hide();
});
scorePanelElement.addEventListener("show.bs.collapse", function () {
evalPanel.hide();
userPanel.hide();
});

document.querySelector("#users-step-finish-button").addEventListener("click", function () {
userPanel.hide();
scorePanel.show();
});
}

function toUsersStep(): void {
interceptAddMultiUserClicks();
initCheckboxes();
document.querySelector("#deadline-group .btn").classList.add("disabled");
document.querySelector("#users-panel").classList.remove("hidden");
document.querySelector("#items-panel").classList.remove("hidden");
evalPanel.hide();
userPanel.show();
}

function interceptAddMultiUserClicks(): void {
let running = false;
document.querySelectorAll(".user-select-option a").forEach(option => {
option.addEventListener("click", async event => {
if (!running) {
running = true;
event.preventDefault();
const button = option.querySelector(".button");
const loader = option.querySelector(".loader");
button.classList.add("hidden");
loader.classList.remove("hidden");
const response = await fetch(option.getAttribute("href"), { method: "POST" });
eval(await response.text());
loader.classList.add("hidden");
button.classList.remove("hidden");
running = false;
}
});
});
}


init();
}
33 changes: 16 additions & 17 deletions app/controllers/evaluations_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ class EvaluationsController < ApplicationController
include SeriesHelper
include EvaluationHelper

before_action :set_evaluation, only: %i[show edit add_users update destroy overview set_multi_user add_user remove_user mark_undecided_complete export_grades modify_grading_visibility]
before_action :set_evaluation, only: %i[show edit update destroy overview set_multi_user add_user remove_user mark_undecided_complete export_grades modify_grading_visibility]
before_action :set_series, only: %i[new]

has_scope :by_institution, as: 'institution_id'
Expand All @@ -16,7 +16,7 @@ class EvaluationsController < ApplicationController
end

def show
redirect_to add_users_evaluation_path(@evaluation) if @evaluation.users.count == 0
redirect_to edit_evaluation_path(@evaluation) if @evaluation.users.count == 0
@feedbacks = @evaluation.evaluation_sheet
@users = apply_scopes(@evaluation.users)
@course_labels = CourseLabel.where(course: @evaluation.series.course)
Expand All @@ -36,6 +36,7 @@ def new
end

def edit
@should_confirm = params[:confirm].present?
@course = @evaluation.series.course
@course_labels = CourseLabel.where(course: @course)
@course_memberships = apply_scopes(@course.course_memberships)
Expand All @@ -57,30 +58,28 @@ def edit
@title = I18n.t('evaluations.edit.title')
end

def add_users
edit
@user_count_course = @evaluation.series.course.enrolled_members.count
@user_count_series = @evaluation.series.course.enrolled_members.where(id: Submission.where(exercise_id: @evaluation.exercises, course_id: @evaluation.series.course_id).select('DISTINCT user_id')).count
@crumbs = [
[@evaluation.series.course.name, course_url(@evaluation.series.course)],
[@evaluation.series.name, breadcrumb_series_path(@evaluation.series, current_user)],
[I18n.t('evaluations.show.evaluation'), evaluation_url(@evaluation)],
[I18n.t('evaluations.add_users.title'), '#']
]
@title = I18n.t('evaluations.add_users.title')
end

def create
@evaluation = Evaluation.new(permitted_attributes(Evaluation))
authorize @evaluation
@evaluation.exercises = @evaluation.series.exercises
@course = @evaluation.series.course
@course_labels = CourseLabel.where(course: @course)
@course_memberships = apply_scopes(@course.course_memberships)
.includes(:course_labels, user: [:institution])
.order(status: :asc)
.order(Arel.sql('users.permission ASC'))
.order(Arel.sql('users.last_name ASC'), Arel.sql('users.first_name ASC'))
.where(status: %i[course_admin student])
.paginate(page: parse_pagination_param(params[:page]))

respond_to do |format|
if @evaluation.save
format.html { redirect_to add_users_evaluation_path(@evaluation) }
@user_count_course = @evaluation.series.course.enrolled_members.count
@user_count_series = @evaluation.series.course.enrolled_members.where(id: Submission.where(exercise_id: @evaluation.exercises, course_id: @evaluation.series.course_id).select('DISTINCT user_id')).count
format.js {}
format.json { render :show, status: :created, location: @evaluation }
else
format.html { render :new }
format.js { render :new }
format.json { render json: @evaluation.errors, status: :unprocessable_entity }
end
end
Expand Down
15 changes: 1 addition & 14 deletions app/controllers/score_items_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,6 @@ class ScoreItemsController < ApplicationController
before_action :set_score_item, only: %i[destroy update]
before_action :set_evaluation

def index
@crumbs << [I18n.t('score_items.index.title'), '#']
@title = I18n.t('score_items.index.title')
end

def new
@crumbs << [I18n.t('score_items.new.title'), '#']
@title = I18n.t('score_items.new.title')
end

def copy
from = EvaluationExercise.find(params[:copy][:from])
to = EvaluationExercise.find(params[:copy][:to])
Expand Down Expand Up @@ -67,12 +57,9 @@ def add_all
@evaluation.transaction do
@evaluation.evaluation_exercises.each do |evaluation_exercise|
new_score_item = @score_item.dup
new_score_item.evaluation_exercise = evaluation_exercise
new_score_item.save
evaluation_exercise.score_items << new_score_item
end
end

redirect_back fallback_location: new_evaluation_score_item_path(@evaluation)
end

def destroy
Expand Down
5 changes: 2 additions & 3 deletions app/javascript/packs/evaluation.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { initDeadlinePicker } from "series.js";
import { interceptAddMultiUserClicks, initCheckboxes, initCheckbox } from "evaluation.ts";
import { initCheckbox, initEvaluationStepper } from "evaluation.ts";
import FeedbackActions from "feedback/actions";

window.dodona.initDeadlinePicker = initDeadlinePicker;
window.dodona.initCheckboxes = initCheckboxes;
window.dodona.initCheckbox = initCheckbox;
window.dodona.interceptAddMultiUserClicks = interceptAddMultiUserClicks;
window.dodona.FeedbackActions = FeedbackActions;
window.dodona.initEvaluationStepper = initEvaluationStepper;
62 changes: 62 additions & 0 deletions app/views/evaluations/_add_users.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<div class="stepper-part evaluation-user-select">
<div class="row">
<div class="col-lg-6 col-md-12 order-lg-1">
<div class="callout callout-info">
<h4><%= t('evaluations.add_users.explanation_title') %></h4>
<p><%= t('evaluations.add_users.explanation_part1') %></p>
<p><%= t('evaluations.add_users.explanation_part2') %></p>
</div>
</div>
<div class="col-lg-6 col-md-12 order-lg-0">
<div class="card-subtitle">
<h4><%= t('evaluations.edit_users.mass_edit') %></h4>
</div>
<div class="row">
<div class="col-6">
<div class="user-select-option">
<%= link_to set_multi_user_evaluation_path(@evaluation, type: 'enrolled', format: :js) do %>
<%= t('evaluations.add_users.users_in_course_html', count: @user_count_course) %>
<div class="clearfix"></div>
<i class="loader mdi mdi-spin mdi-loading hidden"></i>
<div class="button btn-text"><%= t('.select_users') %></div>
<% end %>
</div>
</div>
<div class="col-6">
<div class="user-select-option">
<%= link_to set_multi_user_evaluation_path(@evaluation, type: 'submitted', format: :js) do %>
<%= t('evaluations.add_users.users_submitted_html', count: @user_count_series) %>
<div class="clearfix"></div>
<i class="loader mdi mdi-spin mdi-loading hidden"></i>
<div class="button btn-text"><%= t('.select_users') %></div>
<% end %>
</div>
</div>
</div>
<p class="selected-users">
<span id="users-count-wrapper">
<%= t("evaluations.edit_users.users_selected_html", count: @evaluation.users.count) %>
</span>
<%= link_to t('evaluations.edit_users.clear'), set_multi_user_evaluation_path(@evaluation, type: 'none', format: :js), remote: true, method: :post %>.
</p>
</div>
</div>
</div>
<div class="stepper-part">
<div class="card-supporting-text">
<%= render partial: 'layouts/searchbar', locals: {
baseUrl: edit_evaluation_url(@evaluation),
eager: false,
course_labels: @course_labels,
institutions: Institution.of_course_by_members(@course),
} %>
<div id="users-table-wrapper">
<%= render partial: 'members_table',
locals: {
course_memberships: @course_memberships,
pagination_opts: @pagination_opts,
confirm: false
} %>
</div>
</div>
</div>
14 changes: 0 additions & 14 deletions app/views/evaluations/_edit_users.html.erb

This file was deleted.

4 changes: 2 additions & 2 deletions app/views/evaluations/_form.html.erb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<% content_for :javascripts do %>
<%= javascript_pack_tag 'evaluation' %>
<% end %>
<%= form_for evaluation, html: { class: "form-horizontal feedback-form" } do |f| %>
<%= form_for evaluation, html: { class: "form-horizontal feedback-form" }, remote: true do |f| %>
<div>
<%= f.hidden_field :series_id %>
<h4 class="evaluation-form-title"><%= evaluation.series.name %> <span class="small"><%= evaluation.series.course.name %></span></h4>
Expand All @@ -17,7 +17,7 @@
<div class='col-sm-12'>
<div class="input-group" id='deadline-group' data-wrap=true data-enable-time=true data-time_24hr=true data-max-date="<%= Time.current.httpdate %>">
<%= f.text_field :deadline, class: "form-control", 'data-input': true %>
<button class="btn btn-secondary" type="button" data-toggle><i class='mdi mdi-calendar-blank mdi-18'></i></button>
<button class="btn btn-secondary" type="button" data-toggle <%= "disabled" if local_assigns[:readonly] %>><i class='mdi mdi-calendar-blank mdi-18'></i></button>
</div>
</div>
<span class="help-block col-sm-12"><%= t(".deadline-help_html") %></span>
Expand Down
2 changes: 1 addition & 1 deletion app/views/evaluations/_members_table.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,5 @@
<p class="text-center text-muted lead table-placeholder"><%= t 'users.index.no_users' %></p>
<% end %>
<center>
<%= page_navigation_links course_memberships, true, 'evaluations', action: 'edit' %>
<%= page_navigation_links course_memberships, true, 'evaluations', { id: @evaluation.id }, 'edit' %>
</center>
56 changes: 56 additions & 0 deletions app/views/evaluations/_score_items.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<div class="panel-body">
<div class="stepper-part">
<div class="score-item-toolbar">
<div class="score-item-toolbar-tools">
<p class="description-text">
<%= t 'score_items.new.second_explanation' %><br>
<span class="summary-text">
<%= t('score_items.new.summary_html', count: @evaluation.score_items.count, score: format_score(@evaluation.maximum_score)) %>
</span>
</p>
<div class="btn-group actions">
<a class="btn btn-icon dropdown-toggle" data-bs-toggle="dropdown">
<i class="mdi mdi-dots-vertical"></i>
</a>
<ul class="dropdown-menu dropdown-menu-end">
<li>
<%= link_to modify_grading_visibility_evaluation_path(@evaluation, visible: true), method: :post, class: "dropdown-item" do %>
<i class="mdi mdi-eye mdi-18"></i>
<%= t("score_items.new.show_all") %>
<% end %>
</li>
<li>
<%= link_to modify_grading_visibility_evaluation_path(@evaluation, visible: false), method: :post, class: "dropdown-item" do %>
<i class="mdi mdi-eye-off mdi-18"></i>
<%= t("score_items.new.hide_all") %>
<% end %>
</li>
<li>
<a href="#add-score-item-to-all" data-bs-toggle="modal" class="dropdown-item">
<i class="mdi mdi-table-row-plus-after mdi-18"></i>
<%= t 'score_items.new.add_all' %>
</a>
</li>
</ul>
</div>
</div>
</div>
<% @evaluation.evaluation_exercises.each do |evaluation_exercise| %>
<div id="card-<%= evaluation_exercise.id %>">
<%= render 'score_items/exercise', evaluation_exercise: evaluation_exercise, new: nil %>
</div>
<% end %>
</div>
<div class="stepper-actions stepper-border">
<%= link_to t('score_items.new.to_evaluation'), evaluation_path(@evaluation), class: "btn btn-text btn-primary" %>
</div>
</div>
<div class="modal fade" id="add-score-item-to-all" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<%= render 'score_items/form',
score_item: ScoreItem.new,
evaluation_exercise: nil,
title: t("score_items.new.add_all"),
form_options: { url: add_all_evaluation_score_items_path(@evaluation), remote: true } %>
</div>
</div>
Loading