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
     1.1 --- a/src/main/java/de/uapcore/lightpit/AbstractLightPITServlet.java	Sat Aug 22 16:25:03 2020 +0200
     1.2 +++ b/src/main/java/de/uapcore/lightpit/AbstractLightPITServlet.java	Sat Aug 22 18:17:06 2020 +0200
     1.3 @@ -234,7 +234,7 @@
     1.4       * @param navigationItems the menu entries for the navigation menu
     1.5       * @see Constants#REQ_ATTR_NAVIGATION
     1.6       */
     1.7 -    protected void setNavItems(HttpServletRequest req, List<MenuEntry> navigationItems) {
     1.8 +    protected void setNavigationMenu(HttpServletRequest req, List<MenuEntry> navigationItems) {
     1.9          req.setAttribute(Constants.REQ_ATTR_NAVIGATION, navigationItems);
    1.10      }
    1.11  
     2.1 --- a/src/main/java/de/uapcore/lightpit/MenuEntry.java	Sat Aug 22 16:25:03 2020 +0200
     2.2 +++ b/src/main/java/de/uapcore/lightpit/MenuEntry.java	Sat Aug 22 18:17:06 2020 +0200
     2.3 @@ -50,13 +50,28 @@
     2.4       */
     2.5      private boolean active = false;
     2.6  
     2.7 +    /**
     2.8 +     * The menu level.
     2.9 +     */
    2.10 +    private int level;
    2.11 +
    2.12      public MenuEntry(ResourceKey resourceKey, String pathName) {
    2.13 +        this(0, resourceKey, pathName);
    2.14 +    }
    2.15 +
    2.16 +    public MenuEntry(String text, String pathName) {
    2.17 +        this(0, text, pathName);
    2.18 +    }
    2.19 +
    2.20 +    public MenuEntry(int level, ResourceKey resourceKey, String pathName) {
    2.21 +        this.level = level;
    2.22          this.text = null;
    2.23          this.resourceKey = resourceKey;
    2.24          this.pathName = pathName;
    2.25      }
    2.26  
    2.27 -    public MenuEntry(String text, String pathName) {
    2.28 +    public MenuEntry(int level, String text, String pathName) {
    2.29 +        this.level = level;
    2.30          this.text = text;
    2.31          this.resourceKey = null;
    2.32          this.pathName = pathName;
    2.33 @@ -82,4 +97,11 @@
    2.34          this.active = true;
    2.35      }
    2.36  
    2.37 +    public int getLevel() {
    2.38 +        return level;
    2.39 +    }
    2.40 +
    2.41 +    public void setLevel(int level) {
    2.42 +        this.level = level;
    2.43 +    }
    2.44  }
     3.1 --- a/src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java	Sat Aug 22 16:25:03 2020 +0200
     3.2 +++ b/src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java	Sat Aug 22 18:17:06 2020 +0200
     3.3 @@ -64,6 +64,7 @@
     3.4      public static final String SESSION_ATTR_SELECTED_ISSUE = fqn(ProjectsModule.class, "selected_issue");
     3.5      public static final String SESSION_ATTR_SELECTED_VERSION = fqn(ProjectsModule.class, "selected_version");
     3.6  
     3.7 +    // TODO: try to get rid of this shit
     3.8      private class SessionSelection {
     3.9          final HttpSession session;
    3.10          final HttpServletRequest req;
    3.11 @@ -169,9 +170,13 @@
    3.12          void syncIssue() throws SQLException {
    3.13              final var issueSelection = getParameter(req, Integer.class, "issue");
    3.14              if (issueSelection.isPresent()) {
    3.15 -                final var selectedIssue = dao.getIssueDao().find(issueSelection.get());
    3.16 -                dao.getIssueDao().joinVersionInformation(selectedIssue);
    3.17 -                selectIssue(selectedIssue);
    3.18 +                if (issueSelection.get() < 0) {
    3.19 +                    issue = null;
    3.20 +                } else {
    3.21 +                    final var selectedIssue = dao.getIssueDao().find(issueSelection.get());
    3.22 +                    dao.getIssueDao().joinVersionInformation(selectedIssue);
    3.23 +                    selectIssue(selectedIssue);
    3.24 +                }
    3.25              } else {
    3.26                  issue = issue == null ? null : dao.getIssueDao().find(issue.getId());
    3.27              }
    3.28 @@ -201,75 +206,67 @@
    3.29          return "localization.projects";
    3.30      }
    3.31  
    3.32 -
    3.33 -    private static final int NAV_LEVEL_ROOT = 0;
    3.34 -    private static final int NAV_LEVEL_PROJECT = 1;
    3.35 -    private static final int NAV_LEVEL_VERSION = 2;
    3.36 -    private static final int NAV_LEVEL_ISSUE_LIST = 3;
    3.37 -    private static final int NAV_LEVEL_ISSUE = 4;
    3.38 +    private String queryParams(Project p, Version v, Issue i) {
    3.39 +        return String.format("pid=%d&vid=%d&issue=%d",
    3.40 +                p == null ? -1 : p.getId(),
    3.41 +                v == null ? -1 : v.getId(),
    3.42 +                i == null ? -1 : i.getId()
    3.43 +        );
    3.44 +    }
    3.45  
    3.46      /**
    3.47       * Creates the navigation menu.
    3.48       *
    3.49 -     * @param level     the current active level (0: root, 1: project, 2: version, 3: issue list, 4: issue)
    3.50 +     * @param projects  the list of projects
    3.51       * @param selection the currently selected objects
    3.52 +     * @param projInfo  info about the currently selected project or null
    3.53       * @return a dynamic navigation menu trying to display as many levels as possible
    3.54       */
    3.55 -    private List<MenuEntry> getNavMenu(int level, SessionSelection selection) {
    3.56 -        MenuEntry entry;
    3.57 +    private List<MenuEntry> getNavMenu(List<Project> projects, SessionSelection selection, ProjectInfo projInfo) {
    3.58 +        final var navigation = new ArrayList<MenuEntry>();
    3.59  
    3.60 -        final var navigation = new ArrayList<MenuEntry>();
    3.61 -        entry = new MenuEntry(new ResourceKey("localization.lightpit", "menu.projects"),
    3.62 -                "projects/");
    3.63 -        navigation.add(entry);
    3.64 -        if (level == NAV_LEVEL_ROOT) entry.setActive(true);
    3.65 +        for (Project proj : projects) {
    3.66 +            final var projEntry = new MenuEntry(
    3.67 +                    proj.getName(),
    3.68 +                    "projects/view?pid=" + proj.getId()
    3.69 +            );
    3.70 +            navigation.add(projEntry);
    3.71 +            if (proj.equals(selection.project)) {
    3.72 +                projEntry.setActive(true);
    3.73  
    3.74 -        if (selection.project != null) {
    3.75 -            if (selection.project.getId() < 0) {
    3.76 -                entry = new MenuEntry(new ResourceKey("localization.projects", "button.create"),
    3.77 -                        "projects/edit");
    3.78 -            } else {
    3.79 -                entry = new MenuEntry(selection.project.getName(),
    3.80 -                        "projects/view?pid=" + selection.project.getId());
    3.81 +                // ****************
    3.82 +                // Versions Section
    3.83 +                // ****************
    3.84 +                {
    3.85 +                    final var entry = new MenuEntry(1,
    3.86 +                            new ResourceKey("localization.projects", "menu.versions"),
    3.87 +                            "projects/view?" + queryParams(proj, null, null)
    3.88 +                    );
    3.89 +                    navigation.add(entry);
    3.90 +                }
    3.91 +
    3.92 +                final var level2 = new ArrayList<MenuEntry>();
    3.93 +                {
    3.94 +                    final var entry = new MenuEntry(
    3.95 +                            new ResourceKey("localization.projects", "filter.all"),
    3.96 +                            "projects/view?" + queryParams(proj, null, null)
    3.97 +                    );
    3.98 +                    if (selection.version == null) entry.setActive(true);
    3.99 +                    level2.add(entry);
   3.100 +                }
   3.101 +
   3.102 +                for (Version version : projInfo.getVersions()) {
   3.103 +                    final var entry = new MenuEntry(
   3.104 +                            version.getName(),
   3.105 +                            "projects/versions/view?" + queryParams(proj, version, null)
   3.106 +                    );
   3.107 +                    if (version.equals(selection.version)) entry.setActive(true);
   3.108 +                    level2.add(entry);
   3.109 +                }
   3.110 +
   3.111 +                level2.forEach(e -> e.setLevel(2));
   3.112 +                navigation.addAll(level2);
   3.113              }
   3.114 -            if (level == NAV_LEVEL_PROJECT) entry.setActive(true);
   3.115 -            navigation.add(entry);
   3.116 -        }
   3.117 -
   3.118 -        if (selection.version != null) {
   3.119 -            if (selection.version.getId() < 0) {
   3.120 -                entry = new MenuEntry(new ResourceKey("localization.projects", "button.version.create"),
   3.121 -                        "projects/versions/edit");
   3.122 -            } else {
   3.123 -                entry = new MenuEntry(selection.version.getName(),
   3.124 -                        "projects/versions/view?vid=" + selection.version.getId());
   3.125 -            }
   3.126 -            if (level == NAV_LEVEL_VERSION) entry.setActive(true);
   3.127 -            navigation.add(entry);
   3.128 -        }
   3.129 -
   3.130 -        if (selection.project != null) {
   3.131 -            String path = "projects/issues/?pid=" + selection.project.getId();
   3.132 -            if (selection.version != null) {
   3.133 -                path += "&vid=" + selection.version.getId();
   3.134 -            }
   3.135 -            entry = new MenuEntry(new ResourceKey("localization.projects", "menu.issues"),
   3.136 -                    path);
   3.137 -            if (level == NAV_LEVEL_ISSUE_LIST) entry.setActive(true);
   3.138 -            navigation.add(entry);
   3.139 -        }
   3.140 -
   3.141 -        if (selection.issue != null) {
   3.142 -            if (selection.issue.getId() < 0) {
   3.143 -                entry = new MenuEntry(new ResourceKey("localization.projects", "button.issue.create"),
   3.144 -                        "projects/issues/edit");
   3.145 -            } else {
   3.146 -                entry = new MenuEntry("#" + selection.issue.getId(),
   3.147 -                        // TODO: maybe change link to a view rather than directly opening the editor
   3.148 -                        "projects/issues/edit?issue=" + selection.issue.getId());
   3.149 -            }
   3.150 -            if (level == NAV_LEVEL_ISSUE) entry.setActive(true);
   3.151 -            navigation.add(entry);
   3.152          }
   3.153  
   3.154          return navigation;
   3.155 @@ -297,18 +294,29 @@
   3.156          setContentPage(req, "projects");
   3.157          setStylesheet(req, "projects");
   3.158  
   3.159 -        setNavItems(req, getNavMenu(NAV_LEVEL_ROOT, sessionSelection));
   3.160 +        setNavigationMenu(req, getNavMenu(projectList, sessionSelection, currentProjectInfo(dao, sessionSelection.project)));
   3.161  
   3.162          return ResponseType.HTML;
   3.163      }
   3.164  
   3.165 +    private ProjectInfo currentProjectInfo(DataAccessObjects dao, Project project) throws SQLException {
   3.166 +        if (project == null) return null;
   3.167 +        final var projectDao = dao.getProjectDao();
   3.168 +        final var versionDao = dao.getVersionDao();
   3.169 +
   3.170 +        final var info = new ProjectInfo(project);
   3.171 +        info.setVersions(versionDao.list(project));
   3.172 +        info.setIssueSummary(projectDao.getIssueSummary(project));
   3.173 +        return info;
   3.174 +    }
   3.175 +
   3.176      private ProjectEditView configureEditForm(HttpServletRequest req, DataAccessObjects dao, SessionSelection selection) throws SQLException {
   3.177          final var viewModel = new ProjectEditView();
   3.178          viewModel.setProject(selection.project);
   3.179          viewModel.setUsers(dao.getUserDao().list());
   3.180 +        setNavigationMenu(req, getNavMenu(dao.getProjectDao().list(), selection, currentProjectInfo(dao, selection.project)));
   3.181          setViewModel(req, viewModel);
   3.182          setContentPage(req, "project-form");
   3.183 -        setNavItems(req, getNavMenu(NAV_LEVEL_PROJECT, selection));
   3.184          return viewModel;
   3.185      }
   3.186  
   3.187 @@ -366,6 +374,7 @@
   3.188              return ResponseType.NONE;
   3.189          }
   3.190  
   3.191 +        final var projectDao = dao.getProjectDao();
   3.192          final var versionDao = dao.getVersionDao();
   3.193          final var issueDao = dao.getIssueDao();
   3.194  
   3.195 @@ -373,11 +382,12 @@
   3.196          final var issues = issueDao.list(selection.project);
   3.197          for (var issue : issues) issueDao.joinVersionInformation(issue);
   3.198          viewModel.setIssues(issues);
   3.199 +        // TODO: fix duplicated selection of versions (projectInfo also contains these infos)
   3.200          viewModel.setVersions(versionDao.list(selection.project));
   3.201          viewModel.updateVersionInfo();
   3.202          setViewModel(req, viewModel);
   3.203  
   3.204 -        setNavItems(req, getNavMenu(NAV_LEVEL_PROJECT, selection));
   3.205 +        setNavigationMenu(req, getNavMenu(projectDao.list(), selection, currentProjectInfo(dao, selection.project)));
   3.206          setContentPage(req, "project-details");
   3.207          setStylesheet(req, "projects");
   3.208  
   3.209 @@ -393,14 +403,16 @@
   3.210              return ResponseType.NONE;
   3.211          }
   3.212  
   3.213 +        final var projectDao = dao.getProjectDao();
   3.214          final var issueDao = dao.getIssueDao();
   3.215 +
   3.216          final var viewModel = new VersionView(selection.version);
   3.217          final var issues = issueDao.list(selection.version);
   3.218          for (var issue : issues) issueDao.joinVersionInformation(issue);
   3.219          viewModel.setIssues(issues);
   3.220          setViewModel(req, viewModel);
   3.221  
   3.222 -        setNavItems(req, getNavMenu(NAV_LEVEL_VERSION, selection));
   3.223 +        setNavigationMenu(req, getNavMenu(projectDao.list(), selection, currentProjectInfo(dao, selection.project)));
   3.224          setContentPage(req, "version");
   3.225          setStylesheet(req, "projects");
   3.226  
   3.227 @@ -414,7 +426,7 @@
   3.228          }
   3.229          setViewModel(req, viewModel);
   3.230          setContentPage(req, "version-form");
   3.231 -        setNavItems(req, getNavMenu(NAV_LEVEL_VERSION, selection));
   3.232 +        setNavigationMenu(req, getNavMenu(dao.getProjectDao().list(), selection, currentProjectInfo(dao, selection.project)));
   3.233          return viewModel;
   3.234      }
   3.235  
   3.236 @@ -471,7 +483,7 @@
   3.237          setViewModel(req, viewModel);
   3.238  
   3.239          setContentPage(req, "issue-form");
   3.240 -        setNavItems(req, getNavMenu(NAV_LEVEL_ISSUE, selection));
   3.241 +        setNavigationMenu(req, getNavMenu(dao.getProjectDao().list(), selection, currentProjectInfo(dao, selection.project)));
   3.242          return viewModel;
   3.243      }
   3.244  
   3.245 @@ -484,17 +496,20 @@
   3.246              return ResponseType.NONE;
   3.247          }
   3.248  
   3.249 +        final var projectDao = dao.getProjectDao();
   3.250 +        final var issueDao = dao.getIssueDao();
   3.251 +
   3.252          final var viewModel = new IssuesView();
   3.253          viewModel.setProject(selection.project);
   3.254          if (selection.version == null) {
   3.255 -            viewModel.setIssues(dao.getIssueDao().list(selection.project));
   3.256 +            viewModel.setIssues(issueDao.list(selection.project));
   3.257          } else {
   3.258              viewModel.setVersion(selection.version);
   3.259 -            viewModel.setIssues(dao.getIssueDao().list(selection.version));
   3.260 +            viewModel.setIssues(issueDao.list(selection.version));
   3.261          }
   3.262          setViewModel(req, viewModel);
   3.263  
   3.264 -        setNavItems(req, getNavMenu(NAV_LEVEL_ISSUE_LIST, selection));
   3.265 +        setNavigationMenu(req, getNavMenu(projectDao.list(), selection, currentProjectInfo(dao, selection.project)));
   3.266          setContentPage(req, "issues");
   3.267          setStylesheet(req, "projects");
   3.268  
     4.1 --- a/src/main/resources/localization/projects.properties	Sat Aug 22 16:25:03 2020 +0200
     4.2 +++ b/src/main/resources/localization/projects.properties	Sat Aug 22 18:17:06 2020 +0200
     4.3 @@ -31,6 +31,9 @@
     4.4  
     4.5  no-projects=Welcome to LightPIT. Start off by creating a new project!
     4.6  
     4.7 +filter.all=all
     4.8 +
     4.9 +menu.versions=Versions
    4.10  menu.issues=Issues
    4.11  
    4.12  name=Name
     5.1 --- a/src/main/resources/localization/projects_de.properties	Sat Aug 22 16:25:03 2020 +0200
     5.2 +++ b/src/main/resources/localization/projects_de.properties	Sat Aug 22 18:17:06 2020 +0200
     5.3 @@ -31,6 +31,9 @@
     5.4  
     5.5  no-projects=Wilkommen bei LightPIT. Beginnen Sie mit der Erstellung eines Projektes!
     5.6  
     5.7 +filter.all=alle
     5.8 +
     5.9 +menu.versions=Versionen
    5.10  menu.issues=Vorg\u00e4nge
    5.11  
    5.12  name=Name
     6.1 --- a/src/main/webapp/WEB-INF/jsp/project-form.jsp	Sat Aug 22 16:25:03 2020 +0200
     6.2 +++ b/src/main/webapp/WEB-INF/jsp/project-form.jsp	Sat Aug 22 18:17:06 2020 +0200
     6.3 @@ -28,8 +28,8 @@
     6.4  <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
     6.5  <%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
     6.6  
     6.7 -<jsp:useBean id="project" type="de.uapcore.lightpit.entities.Project" scope="request"/>
     6.8 -<jsp:useBean id="users" type="java.util.List<de.uapcore.lightpit.entities.User>" scope="request"/>
     6.9 +<jsp:useBean id="viewmodel" type="de.uapcore.lightpit.viewmodel.ProjectEditView" scope="request" />
    6.10 +<c:set var="project" scope="page" value="${viewmodel.project}"/>
    6.11  
    6.12  <form action="./projects/commit" method="post">
    6.13      <table class="formtable">
    6.14 @@ -55,7 +55,7 @@
    6.15              <td>
    6.16                  <select name="owner">
    6.17                      <option value="-1"><fmt:message key="placeholder.null-owner"/></option>
    6.18 -                    <c:forEach var="user" items="${users}">
    6.19 +                    <c:forEach var="user" items="${viewmodel.users}">
    6.20                          <option
    6.21                                  <c:if test="${not empty project.owner and user eq project.owner}">selected</c:if>
    6.22                                  value="${user.id}"><c:out value="${user.displayname}"/></option>
     7.1 --- a/src/main/webapp/WEB-INF/jspf/menu-entry.jsp	Sat Aug 22 16:25:03 2020 +0200
     7.2 +++ b/src/main/webapp/WEB-INF/jspf/menu-entry.jsp	Sat Aug 22 18:17:06 2020 +0200
     7.3 @@ -1,4 +1,4 @@
     7.4 -<div class="menuEntry"
     7.5 +<div class="menuEntry level-${menu.level}"
     7.6       <c:if test="${menu.active}">data-active</c:if> >
     7.7      <a href="${menu.pathName}">
     7.8          <c:if test="${empty menu.resourceKey}">
     8.1 --- a/src/main/webapp/lightpit.css	Sat Aug 22 16:25:03 2020 +0200
     8.2 +++ b/src/main/webapp/lightpit.css	Sat Aug 22 18:17:06 2020 +0200
     8.3 @@ -61,7 +61,7 @@
     8.4      flex-flow: column;
     8.5      position: fixed;
     8.6      height: 100%;
     8.7 -    width: 20ch;
     8.8 +    width: 30ch;
     8.9      border-image-source: linear-gradient(to bottom, #606060, rgba(60, 60, 60, .25));
    8.10      border-image-slice: 1;
    8.11      border-right-style: solid;
    8.12 @@ -69,7 +69,7 @@
    8.13  }
    8.14  
    8.15  #content-area.sidebar-spacing {
    8.16 -    margin-left: 20ch;
    8.17 +    margin-left: 30ch;
    8.18  }
    8.19  
    8.20  #mainMenu {
    8.21 @@ -79,6 +79,7 @@
    8.22  
    8.23  #sideMenu {
    8.24      background: #f7f7ff;
    8.25 +    overflow-x: scroll;
    8.26  }
    8.27  
    8.28  #mainMenu .menuEntry {
    8.29 @@ -88,6 +89,14 @@
    8.30      border-right-color: #9095a1;
    8.31  }
    8.32  
    8.33 +#sideMenu .menuEntry {
    8.34 +    padding-top: .25em;
    8.35 +    padding-bottom: .25em;
    8.36 +    border-bottom-style: solid;
    8.37 +    border-bottom-width: 1pt;
    8.38 +    border-bottom-color: #d7d7df;
    8.39 +}
    8.40 +
    8.41  #mainMenu .menuEntry[data-active] {
    8.42      background: #d0d0d5;
    8.43  }
    8.44 @@ -96,6 +105,18 @@
    8.45      background: #e7e7ef
    8.46  }
    8.47  
    8.48 +#sideMenu .level-0 {
    8.49 +    padding-left: .25em;
    8.50 +}
    8.51 +
    8.52 +#sideMenu .level-1 {
    8.53 +    padding-left: .75em;
    8.54 +}
    8.55 +
    8.56 +#sideMenu .level-2 {
    8.57 +    padding-left: 2em;
    8.58 +}
    8.59 +
    8.60  #content-area {
    8.61      padding: 1em;
    8.62  }

mercurial