3494-UI-improvements-grading-templates #21

Merged
ansv7779 merged 17 commits from 3494-UI-improvements-grading-templates into develop 2024-11-26 10:18:55 +01:00
6 changed files with 128 additions and 7 deletions
Showing only changes of commit 942418988a - Show all commits

View File

@ -5,8 +5,11 @@
<div wicket:id="feedback"></div> <div wicket:id="feedback"></div>
<form wicket:id="form"> <form wicket:id="form">
<div wicket:id="editing" class="mb-3"></div> <div wicket:id="editing" class="mb-3"></div>
<div class="position-sticky bottom-0 bg-white p-3 border line-length-limit"> <div class="position-sticky bottom-0 bg-white p-3 border line-length-limit hstack">
<button type="submit" class="btn btn-primary">Save</button> <button type="submit" class="btn btn-primary">Save</button>
<span wicket:id="unsaved_changes_alert" class="text-danger flex-grow-1 text-center" role="alert">
<wicket:message key="unsaved_changes"/>
</span>
</div> </div>
</form> </form>
</wicket:extend> </wicket:extend>

View File

@ -2,9 +2,10 @@ package se.su.dsv.scipro.admin.pages.grading;
import jakarta.inject.Inject; import jakarta.inject.Inject;
import org.apache.wicket.RestartResponseException; import org.apache.wicket.RestartResponseException;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.form.Form; import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.panel.FeedbackPanel; import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.model.Model;
import org.apache.wicket.request.mapper.parameter.PageParameters; import org.apache.wicket.request.mapper.parameter.PageParameters;
import se.su.dsv.scipro.admin.pages.AbstractAdminProjectPage; import se.su.dsv.scipro.admin.pages.AbstractAdminProjectPage;
import se.su.dsv.scipro.grading.GradingReportTemplateService; import se.su.dsv.scipro.grading.GradingReportTemplateService;
@ -12,9 +13,9 @@ import se.su.dsv.scipro.grading.GradingReportTemplateUpdate;
import se.su.dsv.scipro.grading.LocalizedString; import se.su.dsv.scipro.grading.LocalizedString;
import se.su.dsv.scipro.report.DuplicateDateException; import se.su.dsv.scipro.report.DuplicateDateException;
import se.su.dsv.scipro.report.GradingReportTemplate; import se.su.dsv.scipro.report.GradingReportTemplate;
import se.su.dsv.scipro.report.ValidDateMustBeInTheFutureException;
import se.su.dsv.scipro.report.NoSuchTemplateException; import se.su.dsv.scipro.report.NoSuchTemplateException;
import se.su.dsv.scipro.report.TemplateLockedException; import se.su.dsv.scipro.report.TemplateLockedException;
import se.su.dsv.scipro.report.ValidDateMustBeInTheFutureException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -25,6 +26,8 @@ public class AdminGradingTemplateEditPage extends AbstractAdminProjectPage imple
0, 0,
new LocalizedString("", "")); new LocalizedString("", ""));
private final WebMarkupContainer unsavedChangesAlert;
@Inject @Inject
GradingReportTemplateService gradingReportTemplateService; GradingReportTemplateService gradingReportTemplateService;
@ -47,7 +50,8 @@ public class AdminGradingTemplateEditPage extends AbstractAdminProjectPage imple
GradingReportTemplateUpdate update = toUpdate( GradingReportTemplateUpdate update = toUpdate(
editingGradingTemplate); editingGradingTemplate);
gradingReportTemplateService.update(id, update); GradingReportTemplate newTemplate = gradingReportTemplateService.update(id, update);
editingGradingTemplate = new EditingGradingTemplate(newTemplate);
success(getString("template_updated")); success(getString("template_updated"));
} catch (ValidDateMustBeInTheFutureException e) { } catch (ValidDateMustBeInTheFutureException e) {
error(getString("valid_from_must_be_in_the_future", () -> e)); error(getString("valid_from_must_be_in_the_future", () -> e));
@ -61,7 +65,23 @@ public class AdminGradingTemplateEditPage extends AbstractAdminProjectPage imple
} }
}; };
form.add(new EditingGradingTemplateComponentPanel("editing", Model.of(editingGradingTemplate))); unsavedChangesAlert = new WebMarkupContainer("unsaved_changes_alert") {
@Override
protected void onConfigure() {
super.onConfigure();
setVisible(editingGradingTemplate.hasChanges());
}
};
form.add(unsavedChangesAlert);
unsavedChangesAlert.setOutputMarkupPlaceholderTag(true);
form.add(new EditingGradingTemplateComponentPanel("editing", () -> editingGradingTemplate) {
@Override
protected void onTemplateChanged(AjaxRequestTarget target) {
super.onTemplateChanged(target);
target.add(unsavedChangesAlert);
}
});
add(form); add(form);
} }

View File

@ -8,8 +8,10 @@ import java.io.Serializable;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects;
class EditingGradingTemplate implements Serializable { class EditingGradingTemplate implements Serializable {
private EditingGradingTemplate original;
private String note; private String note;
private LocalDate validFrom; private LocalDate validFrom;
private List<Criteria> criteria; private List<Criteria> criteria;
@ -21,6 +23,16 @@ class EditingGradingTemplate implements Serializable {
} }
EditingGradingTemplate(GradingReportTemplate template) { EditingGradingTemplate(GradingReportTemplate template) {
this(template, null);
this.original = new EditingGradingTemplate(template, null);
}
/**
* Private constructor for creating a new instance of EditingGradingTemplate
* to be able to track changes made.
* @param doNotCreateOriginal Only exists to differentiate the signature from the public constructor
*/
private EditingGradingTemplate(GradingReportTemplate template, @SuppressWarnings("unused") Void doNotCreateOriginal) {
this.note = template.getNote(); this.note = template.getNote();
this.validFrom = template.getValidFrom(); this.validFrom = template.getValidFrom();
this.gradeLimits = new GradeLimits(template); this.gradeLimits = new GradeLimits(template);
@ -61,10 +73,28 @@ class EditingGradingTemplate implements Serializable {
.sum(); .sum();
} }
public Boolean hasChanges() {
return !Objects.equals(original, this);
}
public void addCriteria() { public void addCriteria() {
this.criteria.add(new Criteria()); this.criteria.add(new Criteria());
} }
@Override
public boolean equals(Object o) {
return o instanceof EditingGradingTemplate that
&& Objects.equals(note, that.note)
&& Objects.equals(validFrom, that.validFrom)
&& Objects.equals(criteria, that.criteria)
&& Objects.equals(gradeLimits, that.gradeLimits);
}
@Override
public int hashCode() {
return Objects.hash(note, validFrom, criteria, gradeLimits);
}
class Criteria implements Serializable { class Criteria implements Serializable {
enum Flag { enum Flag {
OPPOSITION, REFLECTION OPPOSITION, REFLECTION
@ -150,6 +180,22 @@ class EditingGradingTemplate implements Serializable {
this.pointsRequiredToPass = pointsRequiredToPass; this.pointsRequiredToPass = pointsRequiredToPass;
} }
@Override
public boolean equals(Object o) {
return o instanceof Criteria criterion
&& pointsRequiredToPass == criterion.pointsRequiredToPass
&& Objects.equals(titleSv, criterion.titleSv)
&& Objects.equals(titleEn, criterion.titleEn)
&& Objects.equals(points, criterion.points)
&& flag == criterion.flag
&& type == criterion.type;
}
@Override
public int hashCode() {
return Objects.hash(titleSv, titleEn, points, flag, type, pointsRequiredToPass);
}
class Point implements Serializable { class Point implements Serializable {
private String requirementEn; private String requirementEn;
private String requirementSv; private String requirementSv;
@ -179,6 +225,18 @@ class EditingGradingTemplate implements Serializable {
public void setRequirementSv(String requirementSv) { public void setRequirementSv(String requirementSv) {
this.requirementSv = requirementSv; this.requirementSv = requirementSv;
} }
@Override
public boolean equals(Object o) {
return o instanceof Point point
&& Objects.equals(requirementEn, point.requirementEn)
&& Objects.equals(requirementSv, point.requirementSv);
}
@Override
public int hashCode() {
return Objects.hash(requirementEn, requirementSv);
}
} }
} }
} }

View File

@ -64,6 +64,7 @@ class EditingGradingTemplateComponentPanel extends GenericPanel<EditingGradingTe
public void onClick(AjaxRequestTarget target) { public void onClick(AjaxRequestTarget target) {
editingGradingTemplateModel.getObject().getCriteria().remove(item.getModelObject()); editingGradingTemplateModel.getObject().getCriteria().remove(item.getModelObject());
target.add(EditingGradingTemplateComponentPanel.this); target.add(EditingGradingTemplateComponentPanel.this);
onTemplateChanged(target);
} }
}); });
item.add(new CriteriaEditingPanel("criteria", item.getModel())); item.add(new CriteriaEditingPanel("criteria", item.getModel()));
@ -75,6 +76,7 @@ class EditingGradingTemplateComponentPanel extends GenericPanel<EditingGradingTe
public void onClick(AjaxRequestTarget target) { public void onClick(AjaxRequestTarget target) {
editingGradingTemplateModel.getObject().addCriteria(); editingGradingTemplateModel.getObject().addCriteria();
target.add(EditingGradingTemplateComponentPanel.this); target.add(EditingGradingTemplateComponentPanel.this);
onTemplateChanged(target);
} }
}); });
} }
@ -117,6 +119,7 @@ class EditingGradingTemplateComponentPanel extends GenericPanel<EditingGradingTe
EditingGradingTemplate.Criteria.Type objectSelected) EditingGradingTemplate.Criteria.Type objectSelected)
{ {
// auto save // auto save
onTemplateChanged(target);
} }
}; };
typeChoice.setRequired(true); typeChoice.setRequired(true);
@ -149,6 +152,7 @@ class EditingGradingTemplateComponentPanel extends GenericPanel<EditingGradingTe
EditingGradingTemplate.Criteria.Flag objectSelected) EditingGradingTemplate.Criteria.Flag objectSelected)
{ {
// auto save // auto save
onTemplateChanged(target);
} }
}; };
flagChoice.setNullValid(true); flagChoice.setNullValid(true);
@ -176,6 +180,7 @@ class EditingGradingTemplateComponentPanel extends GenericPanel<EditingGradingTe
EditingGradingTemplate.Criteria.Point newPoint = criteria.new Point(); EditingGradingTemplate.Criteria.Point newPoint = criteria.new Point();
criteria.getPoints().add(newPoint); criteria.getPoints().add(newPoint);
target.add(CriteriaEditingPanel.this); target.add(CriteriaEditingPanel.this);
onTemplateChanged(target);
} }
}); });
} }
@ -210,13 +215,14 @@ class EditingGradingTemplateComponentPanel extends GenericPanel<EditingGradingTe
EditingGradingTemplate.Criteria criteria = CriteriaEditingPanel.this.getModelObject(); EditingGradingTemplate.Criteria criteria = CriteriaEditingPanel.this.getModelObject();
criteria.getPoints().remove(model.getObject()); criteria.getPoints().remove(model.getObject());
target.add(CriteriaEditingPanel.this); target.add(CriteriaEditingPanel.this);
onTemplateChanged(target);
} }
}); });
} }
} }
} }
private static class GradeLimitsPanel extends GenericWebMarkupContainer<GradeLimits> { private class GradeLimitsPanel extends GenericWebMarkupContainer<GradeLimits> {
public GradeLimitsPanel(String id, IModel<GradeLimits> model) { public GradeLimitsPanel(String id, IModel<GradeLimits> model) {
super(id, model); super(id, model);
@ -248,6 +254,7 @@ class EditingGradingTemplateComponentPanel extends GenericPanel<EditingGradingTe
GradeLimits gradeLimits = GradeLimitsPanel.this.getModelObject(); GradeLimits gradeLimits = GradeLimitsPanel.this.getModelObject();
gradeLimits.addNewLimit(); gradeLimits.addNewLimit();
target.add(GradeLimitsPanel.this); target.add(GradeLimitsPanel.this);
onTemplateChanged(target);
} }
}); });
} }
@ -278,13 +285,14 @@ class EditingGradingTemplateComponentPanel extends GenericPanel<EditingGradingTe
GradeLimits gradeLimits = GradeLimitsPanel.this.getModelObject(); GradeLimits gradeLimits = GradeLimitsPanel.this.getModelObject();
gradeLimits.getGradeLimits().remove(model.getObject()); gradeLimits.getGradeLimits().remove(model.getObject());
target.add(GradeLimitsPanel.this); target.add(GradeLimitsPanel.this);
onTemplateChanged(target);
} }
}); });
} }
} }
} }
private static class AutoSave extends AjaxFormComponentUpdatingBehavior { private class AutoSave extends AjaxFormComponentUpdatingBehavior {
public AutoSave() { public AutoSave() {
super("input"); super("input");
} }
@ -292,6 +300,11 @@ class EditingGradingTemplateComponentPanel extends GenericPanel<EditingGradingTe
@Override @Override
protected void onUpdate(AjaxRequestTarget target) { protected void onUpdate(AjaxRequestTarget target) {
// just trigger the ajax call is enough to update the model object // just trigger the ajax call is enough to update the model object
onTemplateChanged(target);
} }
} }
protected void onTemplateChanged(AjaxRequestTarget target) {
// do nothing
}
} }

View File

@ -5,6 +5,7 @@ import se.su.dsv.scipro.report.GradingReportTemplate;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects;
class GradeLimits implements Serializable { class GradeLimits implements Serializable {
private List<GradeLimit> gradeLimits; private List<GradeLimit> gradeLimits;
@ -41,6 +42,19 @@ class GradeLimits implements Serializable {
return gradeLimits; return gradeLimits;
} }
@Override
public boolean equals(Object o) {
return o instanceof GradeLimits that
&& Objects.equals(gradeLimits, that.gradeLimits)
&& Objects.equals(failingGrade, that.failingGrade);
}
@Override
public int hashCode() {
return Objects.hash(gradeLimits, failingGrade);
}
class GradeLimit implements Serializable { class GradeLimit implements Serializable {
private String grade; private String grade;
private int lowerLimit; private int lowerLimit;
@ -60,5 +74,17 @@ class GradeLimits implements Serializable {
public void setLowerLimit(int lowerLimit) { public void setLowerLimit(int lowerLimit) {
this.lowerLimit = lowerLimit; this.lowerLimit = lowerLimit;
} }
@Override
public boolean equals(Object o) {
return o instanceof GradeLimit that
&& lowerLimit == that.lowerLimit
&& Objects.equals(grade, that.grade);
}
@Override
public int hashCode() {
return Objects.hash(grade, lowerLimit);
}
} }
} }

View File

@ -3,3 +3,4 @@ valid_from_must_be_in_the_future=The templates valid date must be in the future.
another_template_exists_for_the_given_date_date=There is already another ${projectType.name} template that becomes valid at ${validFrom}, please pick another date. another_template_exists_for_the_given_date_date=There is already another ${projectType.name} template that becomes valid at ${validFrom}, please pick another date.
template_is_locked=You can not edit templates that have become current. The template you are trying to edit became current at ${validFrom}. template_is_locked=You can not edit templates that have become current. The template you are trying to edit became current at ${validFrom}.
template_created=New template for ${name} created. template_created=New template for ${name} created.
unsaved_changes=The grading template has been changed, unsaved changes will be lost if you do not save.