Sat, 22 Aug 2020 18:17:06 +0200
first part of navigation redesign
--- a/src/main/java/de/uapcore/lightpit/AbstractLightPITServlet.java Sat Aug 22 16:25:03 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/AbstractLightPITServlet.java Sat Aug 22 18:17:06 2020 +0200 @@ -234,7 +234,7 @@ * @param navigationItems the menu entries for the navigation menu * @see Constants#REQ_ATTR_NAVIGATION */ - protected void setNavItems(HttpServletRequest req, List<MenuEntry> navigationItems) { + protected void setNavigationMenu(HttpServletRequest req, List<MenuEntry> navigationItems) { req.setAttribute(Constants.REQ_ATTR_NAVIGATION, navigationItems); }
--- a/src/main/java/de/uapcore/lightpit/MenuEntry.java Sat Aug 22 16:25:03 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/MenuEntry.java Sat Aug 22 18:17:06 2020 +0200 @@ -50,13 +50,28 @@ */ private boolean active = false; + /** + * The menu level. + */ + private int level; + public MenuEntry(ResourceKey resourceKey, String pathName) { + this(0, resourceKey, pathName); + } + + public MenuEntry(String text, String pathName) { + this(0, text, pathName); + } + + public MenuEntry(int level, ResourceKey resourceKey, String pathName) { + this.level = level; this.text = null; this.resourceKey = resourceKey; this.pathName = pathName; } - public MenuEntry(String text, String pathName) { + public MenuEntry(int level, String text, String pathName) { + this.level = level; this.text = text; this.resourceKey = null; this.pathName = pathName; @@ -82,4 +97,11 @@ this.active = true; } + public int getLevel() { + return level; + } + + public void setLevel(int level) { + this.level = level; + } }
--- a/src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java Sat Aug 22 16:25:03 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java Sat Aug 22 18:17:06 2020 +0200 @@ -64,6 +64,7 @@ public static final String SESSION_ATTR_SELECTED_ISSUE = fqn(ProjectsModule.class, "selected_issue"); public static final String SESSION_ATTR_SELECTED_VERSION = fqn(ProjectsModule.class, "selected_version"); + // TODO: try to get rid of this shit private class SessionSelection { final HttpSession session; final HttpServletRequest req; @@ -169,9 +170,13 @@ void syncIssue() throws SQLException { final var issueSelection = getParameter(req, Integer.class, "issue"); if (issueSelection.isPresent()) { - final var selectedIssue = dao.getIssueDao().find(issueSelection.get()); - dao.getIssueDao().joinVersionInformation(selectedIssue); - selectIssue(selectedIssue); + if (issueSelection.get() < 0) { + issue = null; + } else { + final var selectedIssue = dao.getIssueDao().find(issueSelection.get()); + dao.getIssueDao().joinVersionInformation(selectedIssue); + selectIssue(selectedIssue); + } } else { issue = issue == null ? null : dao.getIssueDao().find(issue.getId()); } @@ -201,75 +206,67 @@ return "localization.projects"; } - - private static final int NAV_LEVEL_ROOT = 0; - private static final int NAV_LEVEL_PROJECT = 1; - private static final int NAV_LEVEL_VERSION = 2; - private static final int NAV_LEVEL_ISSUE_LIST = 3; - private static final int NAV_LEVEL_ISSUE = 4; + private String queryParams(Project p, Version v, Issue i) { + return String.format("pid=%d&vid=%d&issue=%d", + p == null ? -1 : p.getId(), + v == null ? -1 : v.getId(), + i == null ? -1 : i.getId() + ); + } /** * Creates the navigation menu. * - * @param level the current active level (0: root, 1: project, 2: version, 3: issue list, 4: issue) + * @param projects the list of projects * @param selection the currently selected objects + * @param projInfo info about the currently selected project or null * @return a dynamic navigation menu trying to display as many levels as possible */ - private List<MenuEntry> getNavMenu(int level, SessionSelection selection) { - MenuEntry entry; - + private List<MenuEntry> getNavMenu(List<Project> projects, SessionSelection selection, ProjectInfo projInfo) { final var navigation = new ArrayList<MenuEntry>(); - entry = new MenuEntry(new ResourceKey("localization.lightpit", "menu.projects"), - "projects/"); - navigation.add(entry); - if (level == NAV_LEVEL_ROOT) entry.setActive(true); - if (selection.project != null) { - if (selection.project.getId() < 0) { - entry = new MenuEntry(new ResourceKey("localization.projects", "button.create"), - "projects/edit"); - } else { - entry = new MenuEntry(selection.project.getName(), - "projects/view?pid=" + selection.project.getId()); - } - if (level == NAV_LEVEL_PROJECT) entry.setActive(true); - navigation.add(entry); - } + for (Project proj : projects) { + final var projEntry = new MenuEntry( + proj.getName(), + "projects/view?pid=" + proj.getId() + ); + navigation.add(projEntry); + if (proj.equals(selection.project)) { + projEntry.setActive(true); + + // **************** + // Versions Section + // **************** + { + final var entry = new MenuEntry(1, + new ResourceKey("localization.projects", "menu.versions"), + "projects/view?" + queryParams(proj, null, null) + ); + navigation.add(entry); + } - if (selection.version != null) { - if (selection.version.getId() < 0) { - entry = new MenuEntry(new ResourceKey("localization.projects", "button.version.create"), - "projects/versions/edit"); - } else { - entry = new MenuEntry(selection.version.getName(), - "projects/versions/view?vid=" + selection.version.getId()); - } - if (level == NAV_LEVEL_VERSION) entry.setActive(true); - navigation.add(entry); - } + final var level2 = new ArrayList<MenuEntry>(); + { + final var entry = new MenuEntry( + new ResourceKey("localization.projects", "filter.all"), + "projects/view?" + queryParams(proj, null, null) + ); + if (selection.version == null) entry.setActive(true); + level2.add(entry); + } - if (selection.project != null) { - String path = "projects/issues/?pid=" + selection.project.getId(); - if (selection.version != null) { - path += "&vid=" + selection.version.getId(); + for (Version version : projInfo.getVersions()) { + final var entry = new MenuEntry( + version.getName(), + "projects/versions/view?" + queryParams(proj, version, null) + ); + if (version.equals(selection.version)) entry.setActive(true); + level2.add(entry); + } + + level2.forEach(e -> e.setLevel(2)); + navigation.addAll(level2); } - entry = new MenuEntry(new ResourceKey("localization.projects", "menu.issues"), - path); - if (level == NAV_LEVEL_ISSUE_LIST) entry.setActive(true); - navigation.add(entry); - } - - if (selection.issue != null) { - if (selection.issue.getId() < 0) { - entry = new MenuEntry(new ResourceKey("localization.projects", "button.issue.create"), - "projects/issues/edit"); - } else { - entry = new MenuEntry("#" + selection.issue.getId(), - // TODO: maybe change link to a view rather than directly opening the editor - "projects/issues/edit?issue=" + selection.issue.getId()); - } - if (level == NAV_LEVEL_ISSUE) entry.setActive(true); - navigation.add(entry); } return navigation; @@ -297,18 +294,29 @@ setContentPage(req, "projects"); setStylesheet(req, "projects"); - setNavItems(req, getNavMenu(NAV_LEVEL_ROOT, sessionSelection)); + setNavigationMenu(req, getNavMenu(projectList, sessionSelection, currentProjectInfo(dao, sessionSelection.project))); return ResponseType.HTML; } + private ProjectInfo currentProjectInfo(DataAccessObjects dao, Project project) throws SQLException { + if (project == null) return null; + final var projectDao = dao.getProjectDao(); + final var versionDao = dao.getVersionDao(); + + final var info = new ProjectInfo(project); + info.setVersions(versionDao.list(project)); + info.setIssueSummary(projectDao.getIssueSummary(project)); + return info; + } + private ProjectEditView configureEditForm(HttpServletRequest req, DataAccessObjects dao, SessionSelection selection) throws SQLException { final var viewModel = new ProjectEditView(); viewModel.setProject(selection.project); viewModel.setUsers(dao.getUserDao().list()); + setNavigationMenu(req, getNavMenu(dao.getProjectDao().list(), selection, currentProjectInfo(dao, selection.project))); setViewModel(req, viewModel); setContentPage(req, "project-form"); - setNavItems(req, getNavMenu(NAV_LEVEL_PROJECT, selection)); return viewModel; } @@ -366,6 +374,7 @@ return ResponseType.NONE; } + final var projectDao = dao.getProjectDao(); final var versionDao = dao.getVersionDao(); final var issueDao = dao.getIssueDao(); @@ -373,11 +382,12 @@ final var issues = issueDao.list(selection.project); for (var issue : issues) issueDao.joinVersionInformation(issue); viewModel.setIssues(issues); + // TODO: fix duplicated selection of versions (projectInfo also contains these infos) viewModel.setVersions(versionDao.list(selection.project)); viewModel.updateVersionInfo(); setViewModel(req, viewModel); - setNavItems(req, getNavMenu(NAV_LEVEL_PROJECT, selection)); + setNavigationMenu(req, getNavMenu(projectDao.list(), selection, currentProjectInfo(dao, selection.project))); setContentPage(req, "project-details"); setStylesheet(req, "projects"); @@ -393,14 +403,16 @@ return ResponseType.NONE; } + final var projectDao = dao.getProjectDao(); final var issueDao = dao.getIssueDao(); + final var viewModel = new VersionView(selection.version); final var issues = issueDao.list(selection.version); for (var issue : issues) issueDao.joinVersionInformation(issue); viewModel.setIssues(issues); setViewModel(req, viewModel); - setNavItems(req, getNavMenu(NAV_LEVEL_VERSION, selection)); + setNavigationMenu(req, getNavMenu(projectDao.list(), selection, currentProjectInfo(dao, selection.project))); setContentPage(req, "version"); setStylesheet(req, "projects"); @@ -414,7 +426,7 @@ } setViewModel(req, viewModel); setContentPage(req, "version-form"); - setNavItems(req, getNavMenu(NAV_LEVEL_VERSION, selection)); + setNavigationMenu(req, getNavMenu(dao.getProjectDao().list(), selection, currentProjectInfo(dao, selection.project))); return viewModel; } @@ -471,7 +483,7 @@ setViewModel(req, viewModel); setContentPage(req, "issue-form"); - setNavItems(req, getNavMenu(NAV_LEVEL_ISSUE, selection)); + setNavigationMenu(req, getNavMenu(dao.getProjectDao().list(), selection, currentProjectInfo(dao, selection.project))); return viewModel; } @@ -484,17 +496,20 @@ return ResponseType.NONE; } + final var projectDao = dao.getProjectDao(); + final var issueDao = dao.getIssueDao(); + final var viewModel = new IssuesView(); viewModel.setProject(selection.project); if (selection.version == null) { - viewModel.setIssues(dao.getIssueDao().list(selection.project)); + viewModel.setIssues(issueDao.list(selection.project)); } else { viewModel.setVersion(selection.version); - viewModel.setIssues(dao.getIssueDao().list(selection.version)); + viewModel.setIssues(issueDao.list(selection.version)); } setViewModel(req, viewModel); - setNavItems(req, getNavMenu(NAV_LEVEL_ISSUE_LIST, selection)); + setNavigationMenu(req, getNavMenu(projectDao.list(), selection, currentProjectInfo(dao, selection.project))); setContentPage(req, "issues"); setStylesheet(req, "projects");
--- a/src/main/resources/localization/projects.properties Sat Aug 22 16:25:03 2020 +0200 +++ b/src/main/resources/localization/projects.properties Sat Aug 22 18:17:06 2020 +0200 @@ -31,6 +31,9 @@ no-projects=Welcome to LightPIT. Start off by creating a new project! +filter.all=all + +menu.versions=Versions menu.issues=Issues name=Name
--- a/src/main/resources/localization/projects_de.properties Sat Aug 22 16:25:03 2020 +0200 +++ b/src/main/resources/localization/projects_de.properties Sat Aug 22 18:17:06 2020 +0200 @@ -31,6 +31,9 @@ no-projects=Wilkommen bei LightPIT. Beginnen Sie mit der Erstellung eines Projektes! +filter.all=alle + +menu.versions=Versionen menu.issues=Vorg\u00e4nge name=Name
--- a/src/main/webapp/WEB-INF/jsp/project-form.jsp Sat Aug 22 16:25:03 2020 +0200 +++ b/src/main/webapp/WEB-INF/jsp/project-form.jsp Sat Aug 22 18:17:06 2020 +0200 @@ -28,8 +28,8 @@ <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> -<jsp:useBean id="project" type="de.uapcore.lightpit.entities.Project" scope="request"/> -<jsp:useBean id="users" type="java.util.List<de.uapcore.lightpit.entities.User>" scope="request"/> +<jsp:useBean id="viewmodel" type="de.uapcore.lightpit.viewmodel.ProjectEditView" scope="request" /> +<c:set var="project" scope="page" value="${viewmodel.project}"/> <form action="./projects/commit" method="post"> <table class="formtable"> @@ -55,7 +55,7 @@ <td> <select name="owner"> <option value="-1"><fmt:message key="placeholder.null-owner"/></option> - <c:forEach var="user" items="${users}"> + <c:forEach var="user" items="${viewmodel.users}"> <option <c:if test="${not empty project.owner and user eq project.owner}">selected</c:if> value="${user.id}"><c:out value="${user.displayname}"/></option>
--- a/src/main/webapp/WEB-INF/jspf/menu-entry.jsp Sat Aug 22 16:25:03 2020 +0200 +++ b/src/main/webapp/WEB-INF/jspf/menu-entry.jsp Sat Aug 22 18:17:06 2020 +0200 @@ -1,4 +1,4 @@ -<div class="menuEntry" +<div class="menuEntry level-${menu.level}" <c:if test="${menu.active}">data-active</c:if> > <a href="${menu.pathName}"> <c:if test="${empty menu.resourceKey}">
--- a/src/main/webapp/lightpit.css Sat Aug 22 16:25:03 2020 +0200 +++ b/src/main/webapp/lightpit.css Sat Aug 22 18:17:06 2020 +0200 @@ -61,7 +61,7 @@ flex-flow: column; position: fixed; height: 100%; - width: 20ch; + width: 30ch; border-image-source: linear-gradient(to bottom, #606060, rgba(60, 60, 60, .25)); border-image-slice: 1; border-right-style: solid; @@ -69,7 +69,7 @@ } #content-area.sidebar-spacing { - margin-left: 20ch; + margin-left: 30ch; } #mainMenu { @@ -79,6 +79,7 @@ #sideMenu { background: #f7f7ff; + overflow-x: scroll; } #mainMenu .menuEntry { @@ -88,6 +89,14 @@ border-right-color: #9095a1; } +#sideMenu .menuEntry { + padding-top: .25em; + padding-bottom: .25em; + border-bottom-style: solid; + border-bottom-width: 1pt; + border-bottom-color: #d7d7df; +} + #mainMenu .menuEntry[data-active] { background: #d0d0d5; } @@ -96,6 +105,18 @@ background: #e7e7ef } +#sideMenu .level-0 { + padding-left: .25em; +} + +#sideMenu .level-1 { + padding-left: .75em; +} + +#sideMenu .level-2 { + padding-left: 2em; +} + #content-area { padding: 1em; }