From e7b38df0eb47281ebab3a454e090219cca1459c7 Mon Sep 17 00:00:00 2001 From: Maximilian Grundke Date: Tue, 3 Jul 2018 16:14:19 +0200 Subject: [PATCH] Add sortable exercise list to exercise collection new/edit page --- .../javascripts/exercise_collections.js.erb | 199 ++++++++++-------- .../exercise_collections/_form.html.slim | 20 ++ config/locales/de.yml | 2 + config/locales/en.yml | 2 + 4 files changed, 140 insertions(+), 83 deletions(-) diff --git a/app/assets/javascripts/exercise_collections.js.erb b/app/assets/javascripts/exercise_collections.js.erb index 3530b636..0784f9ac 100644 --- a/app/assets/javascripts/exercise_collections.js.erb +++ b/app/assets/javascripts/exercise_collections.js.erb @@ -1,102 +1,135 @@ $(function() { if ($.isController('exercise_collections')) { - var data = $('#data').data('working-times'); - var averageWorkingTimeValue = parseFloat($('#data').data('average-working-time')); + var data = $('#data'); + var exerciseList = $('#exercise-list'); - var margin = { top: 30, right: 40, bottom: 30, left: 50 }, - width = 720 - margin.left - margin.right, - height = 500 - margin.top - margin.bottom; + if (data.isPresent()) { + data.data('working-times'); + var averageWorkingTimeValue = parseFloat(data.data('average-working-time')); - var x = d3.scaleBand().range([0, width]); - var y = d3.scaleLinear().range([height, 0]); + var margin = {top: 30, right: 40, bottom: 30, left: 50}, + width = 720 - margin.left - margin.right, + height = 500 - margin.top - margin.bottom; - var xAxis = d3.axisBottom(x); - var yAxisLeft = d3.axisLeft(y); + var x = d3.scaleBand().range([0, width]); + var y = d3.scaleLinear().range([height, 0]); - var tooltip = d3.select("#graph").append("div").attr("class", "exercise-id-tooltip"); + var xAxis = d3.axisBottom(x); + var yAxisLeft = d3.axisLeft(y); - var averageWorkingTime = d3.line() - .x(function (d) { return x(d.index) + x.bandwidth()/2; }) - .y(function () { return y(averageWorkingTimeValue); }); + var tooltip = d3.select("#graph").append("div").attr("class", "exercise-id-tooltip"); - var minWorkingTime = d3.line() - .x(function (d) { return x(d.index) + x.bandwidth()/2; }) - .y(function () { return y(0.1*averageWorkingTimeValue); }); + var averageWorkingTime = d3.line() + .x(function (d) { + return x(d.index) + x.bandwidth() / 2; + }) + .y(function () { + return y(averageWorkingTimeValue); + }); - var maxWorkingTime = d3.line() - .x(function (d) { return x(d.index) + x.bandwidth()/2; }) - .y(function () { return y(2*averageWorkingTimeValue); }); + var minWorkingTime = d3.line() + .x(function (d) { + return x(d.index) + x.bandwidth() / 2; + }) + .y(function () { + return y(0.1 * averageWorkingTimeValue); + }); - var svg = d3.select('#graph') - .append("svg") - .attr("width", width + margin.left + margin.right) - .attr("height", height + margin.top + margin.bottom) - .append("g") - .attr("transform", - "translate(" + margin.left + "," + margin.top + ")"); + var maxWorkingTime = d3.line() + .x(function (d) { + return x(d.index) + x.bandwidth() / 2; + }) + .y(function () { + return y(2 * averageWorkingTimeValue); + }); - // Get the data - data = Object.keys(data).map(function (key, index) { - return { - index: index, - exercise_id: parseInt(key), - working_time: parseFloat(data[key]) - }; - }); + var svg = d3.select('#graph') + .append("svg") + .attr("width", width + margin.left + margin.right) + .attr("height", height + margin.top + margin.bottom) + .append("g") + .attr("transform", + "translate(" + margin.left + "," + margin.top + ")"); - // Scale the range of the data - x.domain(data.map(function (d) { return d.index; })); - y.domain([0, d3.max(data, function (d) { return d.working_time; })]); + // Get the data + data = Object.keys(data).map(function (key, index) { + return { + index: index, + exercise_id: parseInt(key), + working_time: parseFloat(data[key]) + }; + }); - // Add the X Axis - svg.append("g") - .attr("class", "x axis") - .attr("transform", "translate(0," + height + ")") - .call(xAxis); + // Scale the range of the data + x.domain(data.map(function (d) { + return d.index; + })); + y.domain([0, d3.max(data, function (d) { + return d.working_time; + })]); - // Add the Y Axis - svg.append("g") - .attr("class", "y axis") - .style("fill", "steelblue") - .call(yAxisLeft); + // Add the X Axis + svg.append("g") + .attr("class", "x axis") + .attr("transform", "translate(0," + height + ")") + .call(xAxis); - // Draw the bars - svg.selectAll("bar") - .data(data) - .enter() - .append("rect") - .attr("class", "value-bar") - .on("mousemove", function (d){ - tooltip - .style("left", d3.event.pageX - 50 + "px") - .style("top", d3.event.pageY + 50 + "px") - .style("display", "inline-block") - .html("<%= I18n.t('activerecord.models.exercise.one') %> ID: " + d.exercise_id + "
" + - "<%= I18n.t('exercises.statistics.average_worktime') %>: " + d.working_time + "s"); - }) - .on("mouseout", function (){ tooltip.style("display", "none");}) - .on("click", function (d) { - window.location.href = "/exercises/" + d.exercise_id + "/statistics"; - }) - .attr("x", function (d) { return x(d.index); }) - .attr("width", x.bandwidth()) - .attr("y", function (d) { return y(d.working_time); }) - .attr("height", function (d) { return height - y(d.working_time); }); + // Add the Y Axis + svg.append("g") + .attr("class", "y axis") + .style("fill", "steelblue") + .call(yAxisLeft); - // Add the average working time path - svg.append("path") - .datum(data) - .attr("class", "line average-working-time") - .attr("d", averageWorkingTime); + // Draw the bars + svg.selectAll("bar") + .data(data) + .enter() + .append("rect") + .attr("class", "value-bar") + .on("mousemove", function (d) { + tooltip + .style("left", d3.event.pageX - 50 + "px") + .style("top", d3.event.pageY + 50 + "px") + .style("display", "inline-block") + .html("<%= I18n.t('activerecord.models.exercise.one') %> ID: " + d.exercise_id + "
" + + "<%= I18n.t('exercises.statistics.average_worktime') %>: " + d.working_time + "s"); + }) + .on("mouseout", function () { + tooltip.style("display", "none"); + }) + .on("click", function (d) { + window.location.href = "/exercises/" + d.exercise_id + "/statistics"; + }) + .attr("x", function (d) { + return x(d.index); + }) + .attr("width", x.bandwidth()) + .attr("y", function (d) { + return y(d.working_time); + }) + .attr("height", function (d) { + return height - y(d.working_time); + }); - // Add the anomaly paths (min/max average exercise working time) - svg.append("path") - .datum(data) - .attr("class", "line minimum-working-time") - .attr("d", minWorkingTime); - svg.append("path") - .datum(data) - .attr("class", "line maximum-working-time") - .attr("d", maxWorkingTime); + // Add the average working time path + svg.append("path") + .datum(data) + .attr("class", "line average-working-time") + .attr("d", averageWorkingTime); + + // Add the anomaly paths (min/max average exercise working time) + svg.append("path") + .datum(data) + .attr("class", "line minimum-working-time") + .attr("d", minWorkingTime); + svg.append("path") + .datum(data) + .attr("class", "line maximum-working-time") + .attr("d", maxWorkingTime); + } else if (exerciseList.isPresent()) { + var list = $("#sortable"); + list.sortable(); + list.disableSelection(); + } } }); diff --git a/app/views/exercise_collections/_form.html.slim b/app/views/exercise_collections/_form.html.slim index c204db47..b7249267 100644 --- a/app/views/exercise_collections/_form.html.slim +++ b/app/views/exercise_collections/_form.html.slim @@ -15,4 +15,24 @@ .form-group = f.label(t('activerecord.attributes.exercise_collections.exercises')) = f.collection_select(:exercise_ids, exercises, :id, :title, {}, {class: 'form-control', multiple: true}) + + .table-responsive#exercise-list + table.table + thead + tr + th + th = t('activerecord.attributes.exercise_collection_item.exercise') + th = t('activerecord.attributes.exercise.user') + th colspan=2 = t('shared.actions') + tbody#sortable + - @exercise_collection.items.order(:position).each do |item| + tr + td + span.fa.fa-bars + td = item.exercise.title + td = item.exercise.author + td = link_to(t('shared.show'), item.exercise) + td + a.remove-exercise href="#" = t('shared.destroy') + .actions = render('shared/submit_button', f: f, object: @exercise_collection) diff --git a/config/locales/de.yml b/config/locales/de.yml index c3f7dc15..e62dbd47 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -131,6 +131,8 @@ de: user: "Autor" exercise: "Aufgabe" feedback_text: "Feedback Text" + exercise_collection_item: + exercise: "Aufgabe" models: code_harbor_link: one: CodeHarbor-Link diff --git a/config/locales/en.yml b/config/locales/en.yml index 494dd163..842dd346 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -131,6 +131,8 @@ en: user: "Author" exercise: "Exercise" feedback_text: "Feedback Text" + exercise_collection_item: + exercise: "Exercise" models: code_harbor_link: one: CodeHarbor Link