more details

This commit is contained in:
Jörg Henke
2026-01-19 19:21:00 +01:00
parent 2cf5a44cf5
commit 62060311e9
5 changed files with 156 additions and 86 deletions

View File

@@ -30,6 +30,8 @@ public class AppController extends CommonController {
@RolesAllowed("timetrack_user") @RolesAllowed("timetrack_user")
@GetMapping("/projectmanagement/workpackage/{pkWorkpackage}/apps") @GetMapping("/projectmanagement/workpackage/{pkWorkpackage}/apps")
public String getAppsOfWorkpackage(@PathVariable("pkWorkpackage") Integer pkWorkpackage, final Model model) { public String getAppsOfWorkpackage(@PathVariable("pkWorkpackage") Integer pkWorkpackage, final Model model) {
model.addAttribute("bean", service.getWorkpackage(pkWorkpackage));
model.addAttribute("project", service.getProjectOfWorkpackage(pkWorkpackage));
model.addAttribute("apps", service.getAppsOf(pkWorkpackage)); model.addAttribute("apps", service.getAppsOf(pkWorkpackage));
return "/projectmanagement/app/list"; return "/projectmanagement/app/list";
} }

View File

@@ -1,5 +1,6 @@
package de.jottyfan.timetrack.modules.projectmanagement; 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_APP; import static de.jottyfan.timetrack.db.project.Tables.T_APP;
import static de.jottyfan.timetrack.db.project.Tables.T_BUNDLE; import static de.jottyfan.timetrack.db.project.Tables.T_BUNDLE;
import static de.jottyfan.timetrack.db.project.Tables.T_WORKPACKAGE; import static de.jottyfan.timetrack.db.project.Tables.T_WORKPACKAGE;
@@ -25,6 +26,7 @@ import org.springframework.stereotype.Repository;
import de.jottyfan.timetrack.db.project.tables.records.TBundleRecord; import de.jottyfan.timetrack.db.project.tables.records.TBundleRecord;
import de.jottyfan.timetrack.db.project.tables.records.TWorkpackageAppRecord; import de.jottyfan.timetrack.db.project.tables.records.TWorkpackageAppRecord;
import de.jottyfan.timetrack.modules.projectmanagement.model.AppBean; import de.jottyfan.timetrack.modules.projectmanagement.model.AppBean;
import de.jottyfan.timetrack.modules.projectmanagement.model.ProjectBean;
import de.jottyfan.timetrack.modules.projectmanagement.model.WorkpackageAppBean; import de.jottyfan.timetrack.modules.projectmanagement.model.WorkpackageAppBean;
import de.jottyfan.timetrack.modules.projectmanagement.model.WorkpackageBean; import de.jottyfan.timetrack.modules.projectmanagement.model.WorkpackageBean;
@@ -109,7 +111,8 @@ public class AppRepository {
* @return the list of workpackage app linkages * @return the list of workpackage app linkages
*/ */
public List<WorkpackageAppBean> getWorkpackageApps(Integer pkApp) { public List<WorkpackageAppBean> getWorkpackageApps(Integer pkApp) {
return jooq.selectFrom(T_WORKPACKAGE_APP).where(T_WORKPACKAGE_APP.FK_APP.eq(pkApp)).fetchInto(WorkpackageAppBean.class); return jooq.selectFrom(T_WORKPACKAGE_APP).where(T_WORKPACKAGE_APP.FK_APP.eq(pkApp))
.fetchInto(WorkpackageAppBean.class);
} }
/** /**
@@ -156,4 +159,37 @@ public class AppRepository {
jooq.selectFrom(T_BUNDLE).fetchInto(TBundleRecord.class).forEach(b -> map.put(b.getPkBundle(), b)); jooq.selectFrom(T_BUNDLE).fetchInto(TBundleRecord.class).forEach(b -> map.put(b.getPkBundle(), b));
return map; return map;
} }
/**
* get the workpackage
*
* @param pkWorkpackage the ID of the workpackage
* @return the workpackage if found or null
*/
public WorkpackageBean getWorkpackage(Integer pkWorkpackage) {
return jooq
// @formatter:off
.selectFrom(T_WORKPACKAGE)
.where(T_WORKPACKAGE.PK_WORKPACKAGE.eq(pkWorkpackage))
.fetchOneInto(WorkpackageBean.class);
// @formatter:on
}
/**
* get the project of the workpackage
*
* @param pkWorkpackage the ID of the workpackage
* @return the project if found or null
*/
public ProjectBean getProjectOfWorkpackage(Integer pkWorkpackage) {
return jooq
// @formatter:off
.select(T_PROJECT.NAME,
T_PROJECT.DESCRIPTION)
.from(T_WORKPACKAGE)
.leftJoin(T_PROJECT).on(T_PROJECT.PK_PROJECT.eq(T_WORKPACKAGE.FK_PROJECT))
.where(T_WORKPACKAGE.PK_WORKPACKAGE.eq(pkWorkpackage))
.fetchOneInto(ProjectBean.class);
// @formatter:on
}
} }

View File

@@ -8,6 +8,7 @@ import org.springframework.stereotype.Service;
import de.jottyfan.timetrack.db.project.tables.records.TBundleRecord; import de.jottyfan.timetrack.db.project.tables.records.TBundleRecord;
import de.jottyfan.timetrack.modules.projectmanagement.model.AppBean; import de.jottyfan.timetrack.modules.projectmanagement.model.AppBean;
import de.jottyfan.timetrack.modules.projectmanagement.model.ProjectBean;
import de.jottyfan.timetrack.modules.projectmanagement.model.WorkpackageAppBean; import de.jottyfan.timetrack.modules.projectmanagement.model.WorkpackageAppBean;
import de.jottyfan.timetrack.modules.projectmanagement.model.WorkpackageBean; import de.jottyfan.timetrack.modules.projectmanagement.model.WorkpackageBean;
@@ -45,4 +46,11 @@ public class AppService {
return repository.getBundleMap(); return repository.getBundleMap();
} }
public WorkpackageBean getWorkpackage(Integer pkWorkpackage) {
return repository.getWorkpackage(pkWorkpackage);
}
public ProjectBean getProjectOfWorkpackage(Integer pkWorkpackage) {
return repository.getProjectOfWorkpackage(pkWorkpackage);
}
} }

View File

@@ -6,19 +6,24 @@
<body> <body>
<font layout:fragment="title">Projekt</font> <font layout:fragment="title">Projekt</font>
<ul layout:fragment="menu"> <ul layout:fragment="menu">
<li class="nav-item" sec:authorize="hasRole('timetrack_user')"><a class="nav-link btn btn-secondary btn-white-text" th:href="@{/projectmanagement}">zur Projektübersicht</a></li> <li class="nav-item" sec:authorize="hasRole('timetrack_user')">
<a class="nav-link btn btn-secondary btn-white-text" th:href="@{/projectmanagement}">zur Projektübersicht</a>
</li>
</ul> </ul>
<main layout:fragment="content"> <main layout:fragment="content">
<div class="card">
<div class="card-header" th:text="${app.name}"></div>
<div class="card-body">
<div class="container"> <div class="container">
<div class="row"> <div class="row">
<div class="col-2">Name</div>
<div class="col-10 fw-bold" th:text="${app.name}"></div>
<div class="col-2">Beschreibung</div> <div class="col-2">Beschreibung</div>
<div class="col-10" th:text="${app.description}"></div> <div class="col-10" th:text="${app.description}"></div>
<div class="col-2">Basisfunktion</div> <div class="col-2">Basisfunktion</div>
<div class="col-10" th:text="${app.basicFunctionality}"></div> <div class="col-10" th:text="${app.basicFunctionality}"></div>
<div class="col-2">URL der Entwicklung</div> <div class="col-2">URL der Entwicklung</div>
<div class="col-10"><a th:href="${app.repositoryUrl}" target="_blank" th:text="${app.repositoryUrl}"></a></div> <div class="col-10">
<a th:href="${app.repositoryUrl}" target="_blank" th:text="${app.repositoryUrl}"></a>
</div>
<div class="col-2" th:if="${app.fkReplacedByApp}">Ersetzt durch andere App</div> <div class="col-2" th:if="${app.fkReplacedByApp}">Ersetzt durch andere App</div>
<div class="col-10" th:if="${app.fkReplacedByApp}"> <div class="col-10" th:if="${app.fkReplacedByApp}">
<a th:href="@{/projectmanagement/app/{id}/assign(id=${app.fkReplacedByApp})}" th:text="${app.fkReplacedByApp}"></a> <a th:href="@{/projectmanagement/app/{id}/assign(id=${app.fkReplacedByApp})}" th:text="${app.fkReplacedByApp}"></a>
@@ -31,8 +36,9 @@
</th:block> </th:block>
</div> </div>
</div> </div>
<div class="row"> </div>
<div class="col-12"> </div>
<div class="card-footer">
<table class="table table-striped"> <table class="table table-striped">
<thead> <thead>
<tr> <tr>
@@ -43,21 +49,15 @@
</thead> </thead>
<tbody> <tbody>
<tr th:each="p : ${workpackages}"> <tr th:each="p : ${workpackages}">
<td> <td><input type="checkbox" th:checked="${p.isIn(linked)}" /> <a th:href="@{/projectmanagement/workpackage/{w}/apps(w=${p.pkWorkpackage})}" th:text="${p.name}"></a></td>
<input type="checkbox" th:checked="${p.isIn(linked)}" /> <a th:href="@{/projectmanagement/workpackage/{w}/apps(w=${p.pkWorkpackage})}" th:text="${p.name}"></a>
</td>
<td th:text="${p.description}"></td> <td th:text="${p.description}"></td>
<td> <td><a th:href="@{/projectmanagement/workpackage/{w}/app/{a}/toggle(w=${p.pkWorkpackage},a=${app.pkApp})}" class="btn btn-outline-secondary">
<a th:href="@{/projectmanagement/workpackage/{w}/app/{a}/toggle(w=${p.pkWorkpackage},a=${app.pkApp})}" class="btn btn-outline-secondary"> <span th:if="${p.isIn(linked)}">rausschmeißen</span> <span th:unless="${p.isIn(linked)}">zuweisen</span>
<span th:if="${p.isIn(linked)}">rausschmeißen</span> </a></td>
<span th:unless="${p.isIn(linked)}">zuweisen</span>
</a>
</td>
</tr> </tr>
</table> </table>
</div> </div>
</div> </div>
</div>
</main> </main>
</body> </body>
</html> </html>

View File

@@ -11,7 +11,30 @@
</li> </li>
</ul> </ul>
<main layout:fragment="content"> <main layout:fragment="content">
<div class="p-2"> <div class="card">
<div class="card-header" th:if="${bean}">
<span th:text="${project.name}" th:if="${project}"></span>:
<span th:text="${bean.name}"></span>
</div>
<div class="card-body" th:if="${bean}">
<div class="container">
<div class="row">
<div class="col-sm-12 col-md-2" th:if="${project}">Projektdetails</div>
<div class="col-sm-12 col-md-10" th:if="${project}" th:text="${project.description}"></div>
<div class="col-sm-12 col-md-2">Beschreibung</div>
<div class="col-sm-12 col-md-10" th:text="${bean.description}"></div>
<div class="col-sm-12 col-md-2">Milestone</div>
<div class="col-sm-12 col-md-10">
<a th:href="${bean.milestoneUrl}" target="_blank" th:text="${bean.milestoneUrl}" th:if="${bean.milestoneUrl}"></a>
</div>
<div class="col-sm-12 col-md-2">Vertrag</div>
<div class="col-sm-12 col-md-10" th:text="${bean.contractUrl}"></div>
<div class="col-sm-12 col-md-2">Zieldatum</div>
<div class="col-sm-12 col-md-10" th:text="${bean.plannedDuedate}"></div>
</div>
</div>
</div>
<div class="card-footer">
<table id="table" class="table table-striped"> <table id="table" class="table table-striped">
<thead> <thead>
<tr> <tr>
@@ -41,6 +64,7 @@
}); });
</script> </script>
</div> </div>
</div>
</main> </main>
</body> </body>
</html> </html>