Improve the UX when creating groups as a supervisor #123
65
test-data/src/main/java/se/su/dsv/scipro/testdata/populators/GroupCreationUXImprovement.java
vendored
Normal file
65
test-data/src/main/java/se/su/dsv/scipro/testdata/populators/GroupCreationUXImprovement.java
vendored
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
package se.su.dsv.scipro.testdata.populators;
|
||||||
|
|
||||||
|
import jakarta.inject.Inject;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import se.su.dsv.scipro.project.Project;
|
||||||
|
import se.su.dsv.scipro.project.ProjectService;
|
||||||
|
import se.su.dsv.scipro.system.ProjectType;
|
||||||
|
import se.su.dsv.scipro.system.User;
|
||||||
|
import se.su.dsv.scipro.testdata.BaseData;
|
||||||
|
import se.su.dsv.scipro.testdata.Factory;
|
||||||
|
import se.su.dsv.scipro.testdata.TestDataPopulator;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class GroupCreationUXImprovement implements TestDataPopulator {
|
||||||
|
|
||||||
|
private static final String[] STUDENT_NAMES = { "Alice", "Bob", "Charlie", "David", "Emma" };
|
||||||
|
|
||||||
|
private final ProjectService projectService;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public GroupCreationUXImprovement(ProjectService projectService) {
|
||||||
|
this.projectService = projectService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void populate(BaseData baseData, Factory factory) {
|
||||||
|
User supervisor = factory.createSupervisor("Evan");
|
||||||
|
List<User> students = createStudents(factory);
|
||||||
|
for (int i = 1; i <= 20; i++) {
|
||||||
|
projectService.save(createProject(baseData, i, supervisor, students));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<User> createStudents(Factory factory) {
|
||||||
|
return Arrays.stream(STUDENT_NAMES).map(factory::createAuthor).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Project createProject(BaseData baseData, int i, User supervisor, List<User> students) {
|
||||||
|
User author1 = students.get(i % students.size());
|
||||||
|
User author2 = students.get((i + 1) % students.size());
|
||||||
|
|
||||||
|
String title = "Test project " + i;
|
||||||
|
if (i % 6 == 0) {
|
||||||
|
title = title + " with a very long title that makes the project special";
|
||||||
|
}
|
||||||
|
|
||||||
|
ProjectType projectType =
|
||||||
|
switch (i % 3) {
|
||||||
|
case 1 -> baseData.magister();
|
||||||
|
case 2 -> baseData.master();
|
||||||
|
default -> baseData.bachelor();
|
||||||
|
};
|
||||||
|
return Project.builder()
|
||||||
|
.title(title)
|
||||||
|
.projectType(projectType)
|
||||||
|
.startDate(LocalDate.now())
|
||||||
|
.headSupervisor(supervisor)
|
||||||
|
.projectParticipants(Set.of(author1, author2))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
@ -2,85 +2,50 @@
|
|||||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
|
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
|
||||||
<body>
|
<body>
|
||||||
<wicket:panel>
|
<wicket:panel>
|
||||||
<div class="row">
|
<div class="line-length-limit">
|
||||||
<div class="col-lg-12">
|
<form wicket:id="form">
|
||||||
<form wicket:id="form">
|
|
||||||
|
|
||||||
<div class="row">
|
<div wicket:id="feedback"></div>
|
||||||
<div class="col-lg-12">
|
|
||||||
<div wicket:id="feedback"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row">
|
<div class="mb-3">
|
||||||
<div class="col-lg-5 col-md-10">
|
<label wicket:for="title" class="form-label">Title</label>
|
||||||
<label wicket:for="title">Title: </label>
|
<input type="text" wicket:id="title" class="form-control">
|
||||||
<input type="text" wicket:id="title" class="form-control">
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row">
|
<div class="mb-3">
|
||||||
<div class="col-lg-5 col-md-10">
|
<label wicket:for="description" class="form-label">Description</label>
|
||||||
<label wicket:for="description">Description: </label>
|
<textarea wicket:id="description" class="form-control"></textarea>
|
||||||
<textarea wicket:id="description" class="form-control"></textarea>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row">
|
<div class="form-check mb-3">
|
||||||
<div class="col-lg-5 col-md-10">
|
<input class="form-check-input" wicket:id="active" type="checkbox"/>
|
||||||
<div class="form-check">
|
<label class="form-check-label" wicket:for="active">Active</label>
|
||||||
<input class="form-check-input" wicket:id="active" type="checkbox"/>
|
</div>
|
||||||
<label class="form-check-label" wicket:for="active">Active</label>
|
|
||||||
|
<fieldset class="mb-3">
|
||||||
|
<legend>Projects</legend>
|
||||||
|
<div class="group-project-grid">
|
||||||
|
<label wicket:id="available_projects">
|
||||||
|
<div>
|
||||||
|
<input class="form-check-input mt-0" type="checkbox" wicket:id="selected">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div>
|
||||||
</div>
|
<h4 wicket:id="title"></h4>
|
||||||
|
<span wicket:id="type"></span>
|
||||||
<div class="row">
|
<br>
|
||||||
<div class="col-lg-12">
|
Started at <span wicket:id="start_date"></span>
|
||||||
<div wicket:id="wmc">
|
<div wicket:id="authors">
|
||||||
|
<span wicket:id="author"></span>
|
||||||
<div class="row">
|
|
||||||
<div class="col-lg-5 col-md-10">
|
|
||||||
<strong>Add projects to group: </strong>
|
|
||||||
<div wicket:id="projectTypes"></div>
|
|
||||||
<select class="form-select" wicket:id="addProjects"></select>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-lg-12">
|
|
||||||
<strong>Projects in group: </strong>
|
|
||||||
<table class="table table-striped table-hover">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Type</th>
|
|
||||||
<th>Title</th>
|
|
||||||
<th>Authors</th>
|
|
||||||
<th>Remove</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr wicket:id="projects">
|
|
||||||
<td><span wicket:id="type"></span></td>
|
|
||||||
<td><span wicket:id="title"></span></td>
|
|
||||||
<td><div wicket:id="authors">
|
|
||||||
<div wicket:id="author"></div>
|
|
||||||
</div></td>
|
|
||||||
<td><a wicket:id="remove"><span class="fa fa-times"></span></a></td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<div wicket:id="noProjects"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
</fieldset>
|
||||||
<button type="submit" class="btn btn-success" >Save</button>
|
<button type="submit" class="btn btn-success">Save</button>
|
||||||
</form>
|
<button type="submit" wicket:id="save_and_close" class="btn btn-success">Save and close</button>
|
||||||
</div>
|
<button type="submit" wicket:id="save_and_create" class="btn btn-success">Save and create another</button>
|
||||||
|
<a wicket:id="cancel" class="btn btn-outline-secondary">Cancel</a>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</wicket:panel>
|
</wicket:panel>
|
||||||
</body>
|
</body>
|
||||||
|
@ -2,11 +2,10 @@ package se.su.dsv.scipro.group;
|
|||||||
|
|
||||||
import jakarta.inject.Inject;
|
import jakarta.inject.Inject;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import org.apache.wicket.ajax.AjaxRequestTarget;
|
import org.apache.wicket.extensions.model.AbstractCheckBoxModel;
|
||||||
import org.apache.wicket.ajax.markup.html.AjaxLink;
|
|
||||||
import org.apache.wicket.markup.html.WebMarkupContainer;
|
|
||||||
import org.apache.wicket.markup.html.basic.Label;
|
import org.apache.wicket.markup.html.basic.Label;
|
||||||
import org.apache.wicket.markup.html.form.*;
|
import org.apache.wicket.markup.html.form.*;
|
||||||
|
import org.apache.wicket.markup.html.link.BookmarkablePageLink;
|
||||||
import org.apache.wicket.markup.html.list.ListItem;
|
import org.apache.wicket.markup.html.list.ListItem;
|
||||||
import org.apache.wicket.markup.html.list.ListView;
|
import org.apache.wicket.markup.html.list.ListView;
|
||||||
import org.apache.wicket.markup.html.panel.FeedbackPanel;
|
import org.apache.wicket.markup.html.panel.FeedbackPanel;
|
||||||
@ -14,10 +13,6 @@ import org.apache.wicket.markup.html.panel.Panel;
|
|||||||
import org.apache.wicket.model.IModel;
|
import org.apache.wicket.model.IModel;
|
||||||
import org.apache.wicket.model.LambdaModel;
|
import org.apache.wicket.model.LambdaModel;
|
||||||
import org.apache.wicket.model.LoadableDetachableModel;
|
import org.apache.wicket.model.LoadableDetachableModel;
|
||||||
import org.apache.wicket.model.Model;
|
|
||||||
import org.apache.wicket.model.util.ListModel;
|
|
||||||
import se.su.dsv.scipro.components.AjaxCheckBoxMultipleChoice;
|
|
||||||
import se.su.dsv.scipro.components.AjaxDropDownChoice;
|
|
||||||
import se.su.dsv.scipro.components.ListAdapterModel;
|
import se.su.dsv.scipro.components.ListAdapterModel;
|
||||||
import se.su.dsv.scipro.profile.UserLinkPanel;
|
import se.su.dsv.scipro.profile.UserLinkPanel;
|
||||||
import se.su.dsv.scipro.project.Project;
|
import se.su.dsv.scipro.project.Project;
|
||||||
@ -25,8 +20,8 @@ import se.su.dsv.scipro.project.ProjectService;
|
|||||||
import se.su.dsv.scipro.project.ProjectStatus;
|
import se.su.dsv.scipro.project.ProjectStatus;
|
||||||
import se.su.dsv.scipro.project.ProjectTeamMemberRoles;
|
import se.su.dsv.scipro.project.ProjectTeamMemberRoles;
|
||||||
import se.su.dsv.scipro.session.SciProSession;
|
import se.su.dsv.scipro.session.SciProSession;
|
||||||
import se.su.dsv.scipro.system.ProjectType;
|
import se.su.dsv.scipro.supervisor.pages.SupervisorEditGroupPage;
|
||||||
import se.su.dsv.scipro.system.ProjectTypeService;
|
import se.su.dsv.scipro.supervisor.pages.SupervisorMyGroupsPage;
|
||||||
import se.su.dsv.scipro.system.User;
|
import se.su.dsv.scipro.system.User;
|
||||||
|
|
||||||
public class EditGroupPanel extends Panel {
|
public class EditGroupPanel extends Panel {
|
||||||
@ -37,9 +32,6 @@ public class EditGroupPanel extends Panel {
|
|||||||
@Inject
|
@Inject
|
||||||
private GroupService groupService;
|
private GroupService groupService;
|
||||||
|
|
||||||
@Inject
|
|
||||||
private ProjectTypeService projectTypeService;
|
|
||||||
|
|
||||||
public EditGroupPanel(String id, final IModel<Group> model) {
|
public EditGroupPanel(String id, final IModel<Group> model) {
|
||||||
super(id, model);
|
super(id, model);
|
||||||
add(new GroupForm("form", model));
|
add(new GroupForm("form", model));
|
||||||
@ -47,18 +39,46 @@ public class EditGroupPanel extends Panel {
|
|||||||
|
|
||||||
private class GroupForm extends Form<Group> {
|
private class GroupForm extends Form<Group> {
|
||||||
|
|
||||||
private final AjaxDropDownChoice<Project> addProjects;
|
|
||||||
private final ListView<Project> projects;
|
|
||||||
private final List<Project> currentProjects;
|
|
||||||
private final AjaxCheckBoxMultipleChoice<ProjectType> projectTypes;
|
|
||||||
|
|
||||||
public GroupForm(String form, final IModel<Group> model) {
|
public GroupForm(String form, final IModel<Group> model) {
|
||||||
super(form, model);
|
super(form, model);
|
||||||
final FeedbackPanel feedbackPanel = new FeedbackPanel("feedback");
|
final FeedbackPanel feedbackPanel = new FeedbackPanel("feedback");
|
||||||
feedbackPanel.setOutputMarkupId(true);
|
feedbackPanel.setOutputMarkupId(true);
|
||||||
add(feedbackPanel);
|
add(feedbackPanel);
|
||||||
|
|
||||||
currentProjects = new ArrayList<>(getModelObject().getProjects());
|
IModel<List<Project>> availableProjects = LoadableDetachableModel.of(() -> {
|
||||||
|
Set<Project> projects = new HashSet<>();
|
||||||
|
projects.addAll(getAllRelevantProjects());
|
||||||
|
// Have to add the projects that are already in the group to the list of available projects
|
||||||
|
// since they may not be included in the relevant projects if they're inactive or completed.
|
||||||
|
// To allow them to be removed from the group, it will not be possible to add them again.
|
||||||
|
projects.addAll(model.getObject().getProjects());
|
||||||
|
return projects
|
||||||
|
.stream()
|
||||||
|
.sorted(Comparator.comparing(Project::getStartDate).reversed().thenComparing(Project::getTitle))
|
||||||
|
.toList();
|
||||||
|
});
|
||||||
|
add(
|
||||||
|
new ListView<>("available_projects", availableProjects) {
|
||||||
|
@Override
|
||||||
|
protected void populateItem(ListItem<Project> item) {
|
||||||
|
CheckBox checkbox = new CheckBox("selected", new SelectProjectModel(model, item.getModel()));
|
||||||
|
checkbox.setOutputMarkupId(true);
|
||||||
|
item.add(checkbox);
|
||||||
|
item.add(new Label("title", item.getModel().map(Project::getTitle)));
|
||||||
|
item.add(new Label("type", item.getModel().map(Project::getProjectTypeName)));
|
||||||
|
item.add(new Label("start_date", item.getModel().map(Project::getStartDate)));
|
||||||
|
IModel<SortedSet<User>> authors = item.getModel().map(Project::getProjectParticipants);
|
||||||
|
item.add(
|
||||||
|
new ListView<>("authors", new ListAdapterModel<>(authors)) {
|
||||||
|
@Override
|
||||||
|
protected void populateItem(ListItem<User> item) {
|
||||||
|
item.add(new UserLinkPanel("author", item.getModel()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
add(new RequiredTextField<>("title", LambdaModel.of(model, Group::getTitle, Group::setTitle)));
|
add(new RequiredTextField<>("title", LambdaModel.of(model, Group::getTitle, Group::setTitle)));
|
||||||
add(new TextArea<>("description", LambdaModel.of(model, Group::getDescription, Group::setDescription)));
|
add(new TextArea<>("description", LambdaModel.of(model, Group::getDescription, Group::setDescription)));
|
||||||
@ -66,120 +86,64 @@ public class EditGroupPanel extends Panel {
|
|||||||
new CheckBox("active", LambdaModel.of(model, Group::isActive, Group::setActive)).setOutputMarkupId(true)
|
new CheckBox("active", LambdaModel.of(model, Group::isActive, Group::setActive)).setOutputMarkupId(true)
|
||||||
);
|
);
|
||||||
|
|
||||||
final WebMarkupContainer wmc = new WebMarkupContainer("wmc");
|
add(
|
||||||
wmc.setOutputMarkupId(true);
|
new SubmitLink("save_and_close") {
|
||||||
|
|
||||||
projectTypes = projectTypeSelection(wmc);
|
|
||||||
wmc.add(projectTypes);
|
|
||||||
|
|
||||||
addProjects = new AjaxDropDownChoice<>(
|
|
||||||
"addProjects",
|
|
||||||
new Model<>(),
|
|
||||||
getSelectableProjects(currentProjects),
|
|
||||||
new LambdaChoiceRenderer<>(Project::getTitle, Project::getId)
|
|
||||||
) {
|
|
||||||
@Override
|
|
||||||
public void onNewSelection(AjaxRequestTarget target, Project objectSelected) {
|
|
||||||
if (objectSelected != null && !currentProjects.contains(objectSelected)) {
|
|
||||||
currentProjects.add(objectSelected);
|
|
||||||
projects.setList(currentProjects);
|
|
||||||
addProjects.setChoices(getSelectableProjects(currentProjects));
|
|
||||||
target.add(wmc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
addProjects.setRequired(false);
|
|
||||||
addProjects.setNullValid(true);
|
|
||||||
wmc.add(addProjects);
|
|
||||||
|
|
||||||
projects = new ListView<>("projects", new ArrayList<>(currentProjects)) {
|
|
||||||
@Override
|
|
||||||
protected void populateItem(final ListItem<Project> item) {
|
|
||||||
item.add(new Label("type", item.getModel().map(Project::getProjectTypeName)));
|
|
||||||
item.add(new Label("title", item.getModel().map(Project::getTitle)));
|
|
||||||
item.add(
|
|
||||||
new ListView<>(
|
|
||||||
"authors",
|
|
||||||
new ListAdapterModel<>(
|
|
||||||
getLoaded(item.getModelObject()).map(Project::getProjectParticipants)
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
@Override
|
|
||||||
public void populateItem(ListItem<User> item) {
|
|
||||||
item.add(new UserLinkPanel("author", item.getModel()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
item.add(
|
|
||||||
new AjaxLink<>("remove", item.getModel()) {
|
|
||||||
@Override
|
|
||||||
public void onClick(AjaxRequestTarget target) {
|
|
||||||
currentProjects.remove(item.getModelObject());
|
|
||||||
projects.setList(currentProjects);
|
|
||||||
addProjects.setChoices(getSelectableProjects(currentProjects));
|
|
||||||
target.add(wmc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
wmc.add(projects);
|
|
||||||
|
|
||||||
wmc.add(
|
|
||||||
new Label("noProjects", "None") {
|
|
||||||
@Override
|
@Override
|
||||||
protected void onConfigure() {
|
public void onAfterSubmit() {
|
||||||
super.onConfigure();
|
setResponsePage(SupervisorMyGroupsPage.class);
|
||||||
setVisibilityAllowed(currentProjects.isEmpty());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
add(
|
||||||
add(wmc);
|
new SubmitLink("save_and_create") {
|
||||||
}
|
@Override
|
||||||
|
public void onAfterSubmit() {
|
||||||
private AjaxCheckBoxMultipleChoice<ProjectType> projectTypeSelection(final WebMarkupContainer wmc) {
|
setResponsePage(SupervisorEditGroupPage.class);
|
||||||
return new AjaxCheckBoxMultipleChoice<>(
|
}
|
||||||
"projectTypes",
|
|
||||||
projectTypeService.findAllActive(),
|
|
||||||
projectTypeService.findAllActive(),
|
|
||||||
new LambdaChoiceRenderer<>(ProjectType::getName, ProjectType::getId)
|
|
||||||
) {
|
|
||||||
@Override
|
|
||||||
public void onUpdate(AjaxRequestTarget target) {
|
|
||||||
addProjects.setChoices(getSelectableProjects(currentProjects));
|
|
||||||
target.add(wmc);
|
|
||||||
}
|
}
|
||||||
};
|
);
|
||||||
|
add(new BookmarkablePageLink<>("cancel", SupervisorMyGroupsPage.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onSubmit() {
|
protected void onSubmit() {
|
||||||
Group group = getModelObject();
|
Group group = getModelObject();
|
||||||
group.setProjects(new HashSet<>(currentProjects));
|
|
||||||
groupService.save(group);
|
groupService.save(group);
|
||||||
info(getString("saved"));
|
info(getString("saved"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private ListModel<Project> getSelectableProjects(List<Project> currentProjects) {
|
private List<Project> getAllRelevantProjects() {
|
||||||
final ProjectService.Filter filter = new ProjectService.Filter();
|
final ProjectService.Filter filter = new ProjectService.Filter();
|
||||||
filter.setSupervisor(SciProSession.get().getUser());
|
filter.setSupervisor(SciProSession.get().getUser());
|
||||||
filter.setRoles(Collections.singleton(ProjectTeamMemberRoles.CO_SUPERVISOR));
|
filter.setRoles(Collections.singleton(ProjectTeamMemberRoles.CO_SUPERVISOR));
|
||||||
filter.setStatuses(Collections.singletonList(ProjectStatus.ACTIVE));
|
filter.setStatuses(Collections.singletonList(ProjectStatus.ACTIVE));
|
||||||
filter.setProjectTypes(projectTypes.getModelObject());
|
return projectService.findAll(filter);
|
||||||
List<Project> all = projectService.findAll(filter);
|
|
||||||
all.removeAll(currentProjects);
|
|
||||||
all.remove(null);
|
|
||||||
return new ListModel<>(all);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private LoadableDetachableModel<Project> getLoaded(final Project project) {
|
private static final class SelectProjectModel extends AbstractCheckBoxModel {
|
||||||
return new LoadableDetachableModel<>() {
|
|
||||||
@Override
|
private final IModel<Group> groupModel;
|
||||||
protected Project load() {
|
private final IModel<Project> projectModel;
|
||||||
return projectService.findOne(project.getId());
|
|
||||||
}
|
public SelectProjectModel(IModel<Group> groupModel, IModel<Project> projectModel) {
|
||||||
};
|
this.groupModel = groupModel;
|
||||||
|
this.projectModel = projectModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSelected() {
|
||||||
|
return groupModel.getObject().getProjects().contains(projectModel.getObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void select() {
|
||||||
|
groupModel.getObject().getProjects().add(projectModel.getObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unselect() {
|
||||||
|
groupModel.getObject().getProjects().remove(projectModel.getObject());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -607,3 +607,27 @@ th.wicket_orderUp, th.sorting_asc {
|
|||||||
.line-length-limit {
|
.line-length-limit {
|
||||||
max-width: 80em;
|
max-width: 80em;
|
||||||
}
|
}
|
||||||
|
.group-project-grid {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 1em;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(30em, 1fr));
|
||||||
|
}
|
||||||
|
.group-project-grid > * {
|
||||||
|
background: linear-gradient(to left, white 40%, var(--bs-success-bg-subtle) 60%) right;
|
||||||
|
background-size: 250% 100%;
|
||||||
|
transition: background 0.4s ease;
|
||||||
|
cursor: pointer;
|
||||||
|
border: 1px solid black;
|
||||||
|
border-radius: 0.25em;
|
||||||
|
display: flex;
|
||||||
|
padding: 0.5em;
|
||||||
|
align-items: center;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
.group-project-grid > *:has(:checked) {
|
||||||
|
background-position: left;
|
||||||
|
}
|
||||||
|
.group-project-grid label {
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
@ -27,7 +27,6 @@ public class EditGroupPanelTest extends SciProTest {
|
|||||||
group.setId(1L);
|
group.setId(1L);
|
||||||
Project project = createProject();
|
Project project = createProject();
|
||||||
group.setProjects(new HashSet<>(Collections.singletonList(project)));
|
group.setProjects(new HashSet<>(Collections.singletonList(project)));
|
||||||
when(projectService.findOne(anyLong())).thenReturn(project);
|
|
||||||
startPanel();
|
startPanel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user