User alert on unsaved changes in grading template
Grading template did not have any way to alert the user that changes had been made when editing a template. Added a way to detect and alert the user when changes have been made on the grading template they are editing. If the user removes his changes, so the template is in the same state as it was when the user started editing the template the alert will disappear.
This commit is contained in:
parent
73307096c3
commit
4b113715d6
view/src/main/java/se/su/dsv/scipro/admin/pages/grading
@ -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>
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
17
view/src/main/java/se/su/dsv/scipro/admin/pages/grading/EditingGradingTemplateComponentPanel.java
17
view/src/main/java/se/su/dsv/scipro/admin/pages/grading/EditingGradingTemplateComponentPanel.java
@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user