first steps for a project management module
This commit is contained in:
@@ -1,26 +1,42 @@
|
||||
package de.jottyfan.timetrack;
|
||||
|
||||
import static de.jottyfan.timetrack.db.public_.Tables.V_VERSION;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jooq.DSLContext;
|
||||
import org.jooq.exception.DataAccessException;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableTransactionManagement
|
||||
public class Main extends SpringBootServletInitializer {
|
||||
|
||||
public static final Logger LOGGER = LogManager.getLogger(Main.class);
|
||||
private static final Integer requiredDbVersion = 20260114;
|
||||
|
||||
public static final Logger LOGGER = LogManager.getLogger(Main.class);
|
||||
|
||||
@Override
|
||||
protected SpringApplicationBuilder configure(
|
||||
SpringApplicationBuilder application) {
|
||||
return application.sources(Main.class);
|
||||
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
|
||||
ConfigurableApplicationContext context = builder.run();
|
||||
DSLContext jooq = context.getBean(DSLContext.class);
|
||||
Integer foundDbVersion = null;
|
||||
try {
|
||||
foundDbVersion = jooq.selectFrom(V_VERSION).fetchOne(V_VERSION.VERSION);
|
||||
} catch (DataAccessException e) {
|
||||
String msg = String.format("Wrong database version found; %d is required, but found %d", requiredDbVersion,
|
||||
foundDbVersion);
|
||||
throw new RuntimeException(msg);
|
||||
}
|
||||
return builder.sources(Main.class);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Main.class, args);
|
||||
}
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Main.class, args);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package de.jottyfan.timetrack.help;
|
||||
|
||||
import static de.jottyfan.timetrack.db.public_.Tables.V_VERSION;
|
||||
|
||||
import org.jooq.DSLContext;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jotty
|
||||
*
|
||||
*/
|
||||
@Repository
|
||||
public class MainfestRepository {
|
||||
@Autowired
|
||||
private DSLContext jooq;
|
||||
|
||||
/**
|
||||
* get the db version with title
|
||||
*
|
||||
* @return the DB version, prefixed by DBVersion
|
||||
*/
|
||||
public String getDbVersion() {
|
||||
return String.format("DBVersion %s", jooq.selectFrom(V_VERSION).fetchOne(V_VERSION.VERSION));
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,14 @@ public class ManifestBean {
|
||||
|
||||
@Autowired(required = false)
|
||||
private BuildProperties buildProperties;
|
||||
|
||||
@Autowired
|
||||
private MainfestRepository repository;
|
||||
|
||||
public String getDbVersion() {
|
||||
return repository.getDbVersion();
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append("Version ");
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
package de.jottyfan.timetrack.modules.projectmanagement;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
|
||||
import de.jottyfan.timetrack.modules.CommonController;
|
||||
import de.jottyfan.timetrack.modules.projectmanagement.model.ProjectBean;
|
||||
import de.jottyfan.timetrack.modules.projectmanagement.model.WorkpackageBean;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jotty
|
||||
*
|
||||
*/
|
||||
@Controller
|
||||
public class ProjectmanagementController extends CommonController {
|
||||
|
||||
@Autowired
|
||||
private ProjectmanagementService service;
|
||||
|
||||
@RolesAllowed("timetrack_user")
|
||||
@GetMapping("/projectmanagement")
|
||||
public String getDashboard(final Model model) {
|
||||
model.addAttribute("projects", service.getProjects());
|
||||
return "/projectmanagement/dashboard";
|
||||
}
|
||||
|
||||
@RolesAllowed("timetrack_user")
|
||||
@GetMapping("/projectmanagement/project/add")
|
||||
public String getProjectAddMask(final Model model) {
|
||||
model.addAttribute("bean", new ProjectBean());
|
||||
return "/projectmanagement/project/add";
|
||||
}
|
||||
|
||||
@RolesAllowed("timetrack_user")
|
||||
@GetMapping("/projectmanagement/workpackage/{pkWorkpackage}")
|
||||
public String getWorkpackage(@PathVariable("pkWorkpackage") Integer pkWorkpackage, final Model model) {
|
||||
model.addAttribute("bean", service.getWorkpackage(pkWorkpackage));
|
||||
return "/projectmanagement/workpackage/item";
|
||||
}
|
||||
|
||||
@RolesAllowed("timetrack_user")
|
||||
@GetMapping("/projectmanagement/project/{fkProject}/addWorkpackage")
|
||||
public String getWorkpackageAddMask(@PathVariable("fkProject") Integer fkProject, final Model model) {
|
||||
model.addAttribute("bean", WorkpackageBean.of(fkProject));
|
||||
return "/projectmanagement/workpackage/item";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package de.jottyfan.timetrack.modules.projectmanagement;
|
||||
|
||||
import static de.jottyfan.timetrack.db.project.Tables.T_PROJECT;
|
||||
import static de.jottyfan.timetrack.db.project.Tables.T_WORKPACKAGE;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.jooq.DSLContext;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import de.jottyfan.timetrack.db.project.tables.records.TProjectRecord;
|
||||
import de.jottyfan.timetrack.db.project.tables.records.TWorkpackageRecord;
|
||||
import de.jottyfan.timetrack.modules.projectmanagement.model.ProjectBean;
|
||||
import de.jottyfan.timetrack.modules.projectmanagement.model.WorkpackageBean;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jotty
|
||||
*
|
||||
*/
|
||||
@Repository
|
||||
public class ProjectmanagementRepository {
|
||||
|
||||
@Autowired
|
||||
private DSLContext jooq;
|
||||
|
||||
public List<ProjectBean> getProjects() {
|
||||
List<TProjectRecord> projects = jooq.selectFrom(T_PROJECT).fetchInto(TProjectRecord.class);
|
||||
List<TWorkpackageRecord> workpackages = jooq.selectFrom(T_WORKPACKAGE).fetchInto(TWorkpackageRecord.class);
|
||||
Map<Integer, List<WorkpackageBean>> wpMap = new HashMap<>();
|
||||
workpackages.forEach(w -> {
|
||||
List<WorkpackageBean> found = wpMap.get(w.getFkProject());
|
||||
if (found == null) {
|
||||
found = new ArrayList<>();
|
||||
wpMap.put(w.getFkProject(), found);
|
||||
}
|
||||
found.add(WorkpackageBean.of(w));
|
||||
});
|
||||
List<ProjectBean> list = new ArrayList<>();
|
||||
for (TProjectRecord r : projects) {
|
||||
list.add(ProjectBean.of(r, wpMap.get(r.getPkProject())));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public WorkpackageBean getWorkpackage(Integer pkWorkpackage) {
|
||||
return jooq.selectFrom(T_WORKPACKAGE).where(T_WORKPACKAGE.PK_WORKPACKAGE.eq(pkWorkpackage)).fetchOneInto(WorkpackageBean.class);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package de.jottyfan.timetrack.modules.projectmanagement;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import de.jottyfan.timetrack.modules.projectmanagement.model.ProjectBean;
|
||||
import de.jottyfan.timetrack.modules.projectmanagement.model.WorkpackageBean;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jotty
|
||||
*
|
||||
*/
|
||||
@Service
|
||||
public class ProjectmanagementService {
|
||||
|
||||
@Autowired
|
||||
private ProjectmanagementRepository repository;
|
||||
|
||||
/**
|
||||
* get all projects
|
||||
*
|
||||
* @return all projects
|
||||
*/
|
||||
public List<ProjectBean> getProjects() {
|
||||
return repository.getProjects();
|
||||
}
|
||||
|
||||
/**
|
||||
* get the workpackage
|
||||
*
|
||||
* @param pkWorkpackage the ID of the workpackage
|
||||
* @return the bean or null if not found
|
||||
*/
|
||||
public WorkpackageBean getWorkpackage(Integer pkWorkpackage) {
|
||||
return repository.getWorkpackage(pkWorkpackage);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package de.jottyfan.timetrack.modules.projectmanagement.model;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import de.jottyfan.timetrack.db.project.tables.records.TProjectRecord;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jotty
|
||||
*
|
||||
*/
|
||||
public class ProjectBean implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Integer pkProject;
|
||||
private String name;
|
||||
private String description;
|
||||
private final List<WorkpackageBean> workpackages;
|
||||
|
||||
public ProjectBean() {
|
||||
workpackages = new ArrayList<>();
|
||||
}
|
||||
|
||||
public static final ProjectBean of(TProjectRecord r, List<WorkpackageBean> workpackages) {
|
||||
ProjectBean bean = new ProjectBean();
|
||||
bean.setPkProject(r.getPkProject());
|
||||
bean.setName(r.getName());
|
||||
bean.setDescription(r.getDescription());
|
||||
if (workpackages != null) {
|
||||
bean.getWorkpackages().addAll(workpackages);
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the name
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name the name to set
|
||||
*/
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the description
|
||||
*/
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param description the description to set
|
||||
*/
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the workpackages
|
||||
*/
|
||||
public List<WorkpackageBean> getWorkpackages() {
|
||||
return workpackages;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the pkProject
|
||||
*/
|
||||
public Integer getPkProject() {
|
||||
return pkProject;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param pkProject the pkProject to set
|
||||
*/
|
||||
public void setPkProject(Integer pkProject) {
|
||||
this.pkProject = pkProject;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package de.jottyfan.timetrack.modules.projectmanagement.model;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import de.jottyfan.timetrack.db.project.tables.records.TWorkpackageRecord;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jotty
|
||||
*
|
||||
*/
|
||||
public class WorkpackageBean implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Integer pkWorkpackage;
|
||||
private String name;
|
||||
private Integer fkProject;
|
||||
|
||||
public static final WorkpackageBean of(TWorkpackageRecord r) {
|
||||
WorkpackageBean bean = new WorkpackageBean();
|
||||
bean.setPkWorkpackage(r.getPkWorkpackage());
|
||||
bean.setName(r.getName());
|
||||
bean.setFkProject(r.getFkProject());
|
||||
return bean;
|
||||
}
|
||||
|
||||
public static final WorkpackageBean of(Integer fkProject) {
|
||||
WorkpackageBean bean = new WorkpackageBean();
|
||||
bean.setFkProject(fkProject);
|
||||
return bean;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the name
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name the name to set
|
||||
*/
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the pkWorkpackage
|
||||
*/
|
||||
public Integer getPkWorkpackage() {
|
||||
return pkWorkpackage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param pkWorkpackage the pkWorkpackage to set
|
||||
*/
|
||||
public void setPkWorkpackage(Integer pkWorkpackage) {
|
||||
this.pkWorkpackage = pkWorkpackage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the fkProject
|
||||
*/
|
||||
public Integer getFkProject() {
|
||||
return fkProject;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fkProject the fkProject to set
|
||||
*/
|
||||
public void setFkProject(Integer fkProject) {
|
||||
this.fkProject = fkProject;
|
||||
}
|
||||
}
|
||||
@@ -242,15 +242,20 @@ body {
|
||||
|
||||
.version {
|
||||
font-size: small;
|
||||
color: black;
|
||||
color: gray;
|
||||
position: absolute;
|
||||
padding-top: 36px;
|
||||
padding-left: 22px;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
[data-bs-theme="dark"] .version {
|
||||
color: white;
|
||||
.dbversion {
|
||||
font-size: small;
|
||||
color: gray;
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
padding-left: 22px;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.fc-content {
|
||||
@@ -494,3 +499,8 @@ body {
|
||||
.boldy {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
.dashboardcard {
|
||||
width: 312px !important;
|
||||
margin: 24px !important;
|
||||
}
|
||||
@@ -26,6 +26,7 @@
|
||||
<body>
|
||||
<nav class="navbar navbar-expand-lg static-top">
|
||||
<div class="container-fluid" style="width: 98%">
|
||||
<div class="dbversion" th:text="${@manifestBean.getDbVersion()}"></div>
|
||||
<i class="fa fa-calendar-alt"></i> <a class="navbar-brand" style="margin-left: 8px; z-index: 1" th:href="@{/}">Timetrack</a><br />
|
||||
<div class="version" th:text="${@manifestBean.getVersion()}"></div>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarResponsive"
|
||||
@@ -41,6 +42,7 @@
|
||||
<li><a class="dropdown-item" th:href="@{/contact/list}">Kontakte</a></li>
|
||||
<li><a class="dropdown-item" th:href="@{/note/list}">Notizen</a></li>
|
||||
<li><a class="dropdown-item" th:href="@{/calendar}">Kalender</a></li>
|
||||
<li><a class="dropdown-item" th:href="@{/projectmanagement}">Projekte</a></li>
|
||||
<li><hr /></li>
|
||||
<li><a class="dropdown-item" th:href="@{/logout}">[[${currentUser}]] abmelden</a></li>
|
||||
</ul></li>
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:sec="http://www.thymeleaf.org/extras/spring-security" layout:decorate="~{layout/main.html}">
|
||||
<head>
|
||||
<title>Projektmanagement</title>
|
||||
</head>
|
||||
<body>
|
||||
<font layout:fragment="title">Projekte</font>
|
||||
<ul layout:fragment="menu">
|
||||
<li class="nav-item" sec:authorize="hasRole('timetrack_user')">
|
||||
<a class="nav-link btn btn-success btn-white-text" th:href="@{/projectmanagement/project/add}">Neues Projekt anlegen</a>
|
||||
</li>
|
||||
</ul>
|
||||
<main layout:fragment="content">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-4 col-lg-2 card dashboardcard" th:each="p : ${projects}">
|
||||
<div class="card-header" th:text="${p.name}"></div>
|
||||
<div class="card-body">
|
||||
<div th:each="w : ${p.workpackages}">
|
||||
<a th:href="@{/projectmanagement/workpackage/{id}(id=${w.pkWorkpackage})}" th:text="${w.name}"></a>
|
||||
</div>
|
||||
<a th:href="@{/projectmanagement/project/{id}/addWorkpackage(id=${p.pkProject})}" class="btn btn-outline-secondary">Workpackage anlegen</a>
|
||||
</div>
|
||||
<div class="card-footer" th:text="${p.description}"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,19 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:sec="http://www.thymeleaf.org/extras/spring-security" layout:decorate="~{layout/main.html}">
|
||||
<head>
|
||||
<title>Projektmanagement</title>
|
||||
</head>
|
||||
<body>
|
||||
<font layout:fragment="title">Projekt</font>
|
||||
<ul layout:fragment="menu">
|
||||
<li class="nav-item" sec:authorize="hasRole('timetrack_user')">
|
||||
<a class="nav-link btn btn-success btn-white-text" th:href="@{/projectmanagement}">abbrechen</a>
|
||||
</li>
|
||||
</ul>
|
||||
<main layout:fragment="content">
|
||||
<div class="container">
|
||||
TODO: Maske zum Anlegen eines neuen Projektes
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,32 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:sec="http://www.thymeleaf.org/extras/spring-security" layout:decorate="~{layout/main.html}">
|
||||
<head>
|
||||
<title>Projektmanagement</title>
|
||||
</head>
|
||||
<body>
|
||||
<font layout:fragment="title">Workpackage</font>
|
||||
<ul layout:fragment="menu">
|
||||
<li class="nav-item" sec:authorize="hasRole('timetrack_user')">
|
||||
<a class="nav-link btn btn-success btn-white-text" th:href="@{/projectmanagement}">abbrechen</a>
|
||||
</li>
|
||||
</ul>
|
||||
<main layout:fragment="content">
|
||||
<div class="container">
|
||||
<div th:unless="${bean.pkWorkpackage}">
|
||||
TODO: Maske zum Anlegen eines neuen Workpackages für Projekt <span th:text="${bean.fkProject}"></span>
|
||||
</div>
|
||||
<div th:if="${bean.pkWorkpackage}">
|
||||
<div class="container m-2">
|
||||
<div class="row g-2">
|
||||
<div class="col-2">ID</div>
|
||||
<div class="col-10"><span class="form-control" th:text="${bean.pkWorkpackage}"></span></div>
|
||||
<div class="col-2">Name</div>
|
||||
<div class="col-10"><span class="form-control" th:text="${bean.name}"></span></div>
|
||||
</div>
|
||||
</div>
|
||||
TODO: Maske zum Bearbeiten eines bestehenden Workpackages
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
@@ -10,7 +10,7 @@
|
||||
<ul layout:fragment="menu">
|
||||
</ul>
|
||||
<main layout:fragment="content">
|
||||
<div class="card" style="width: 312px; margin: 24px">
|
||||
<div class="card dashboardcard">
|
||||
<div class="card-header"><a class="btn btn-seondary btn-bordered btn-secondaryhover" style="width: 100%"
|
||||
th:href="@{/done/list}">heutige Arbeitszeiten</a></div>
|
||||
<div class="card-body">
|
||||
|
||||
Reference in New Issue
Block a user