first part of navigation redesign

Sat, 22 Aug 2020 18:17:06 +0200

author
Mike Becker <universe@uap-core.de>
date
Sat, 22 Aug 2020 18:17:06 +0200
changeset 97
602f75801644
parent 96
b7b685f31e39
child 98
5c406eef0e5c

first part of navigation redesign

src/main/java/de/uapcore/lightpit/AbstractLightPITServlet.java file | annotate | diff | comparison | revisions
src/main/java/de/uapcore/lightpit/MenuEntry.java file | annotate | diff | comparison | revisions
src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java file | annotate | diff | comparison | revisions
src/main/resources/localization/projects.properties file | annotate | diff | comparison | revisions
src/main/resources/localization/projects_de.properties file | annotate | diff | comparison | revisions
src/main/webapp/WEB-INF/jsp/project-form.jsp file | annotate | diff | comparison | revisions
src/main/webapp/WEB-INF/jspf/menu-entry.jsp file | annotate | diff | comparison | revisions
src/main/webapp/lightpit.css file | annotate | diff | comparison | revisions
--- 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;
 }

mercurial