preparations for app assignment
This commit is contained in:
@@ -45,7 +45,7 @@ public class SecurityConfiguration {
|
||||
// @formatter:off
|
||||
.oauth2Login(o -> o.defaultSuccessUrl("/"))
|
||||
.logout(o -> o.logoutSuccessHandler(new OidcClientInitiatedLogoutSuccessHandler(crr)))
|
||||
.authorizeHttpRequests(o -> o.requestMatchers("/public/**", "/theme/**").permitAll().anyRequest().authenticated())
|
||||
.authorizeHttpRequests(o -> o.requestMatchers("/public/**", "/theme/**", "/webjars/**").permitAll().anyRequest().authenticated())
|
||||
.oauth2ResourceServer(o -> o.jwt(Customizer.withDefaults()))
|
||||
.sessionManagement(o -> o.init(sec));
|
||||
// @formatter:on
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
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 jakarta.annotation.security.RolesAllowed;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jotty
|
||||
*
|
||||
*/
|
||||
@Controller
|
||||
public class AppController extends CommonController {
|
||||
|
||||
@Autowired
|
||||
private AppService service;
|
||||
|
||||
@RolesAllowed("timetrack_user")
|
||||
@GetMapping("/projectmanagement/apps")
|
||||
public String getAllApps(final Model model) {
|
||||
model.addAttribute("apps", service.getAppsOf(null));
|
||||
return "/projectmanagement/app/list";
|
||||
}
|
||||
|
||||
@RolesAllowed("timetrack_user")
|
||||
@GetMapping("/projectmanagement/workpackage/{pkWorkpackage}/apps")
|
||||
public String getAppsOfWorkpackage(@PathVariable("pkWorkpackage") Integer pkWorkpackage, final Model model) {
|
||||
model.addAttribute("apps", service.getAppsOf(pkWorkpackage));
|
||||
return "/projectmanagement/app/list";
|
||||
}
|
||||
|
||||
@RolesAllowed("timetrack_user")
|
||||
@GetMapping("/projectmanagement/app/{pkApp}/assign")
|
||||
public String loadAssignmentToolForApp(@PathVariable("pkApp") Integer pkApp, final Model model) {
|
||||
model.addAttribute("app", service.getApp(pkApp));
|
||||
model.addAttribute("workpackages", service.getWorkpackages());
|
||||
return "/projectmanagement/app/assign";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package de.jottyfan.timetrack.modules.projectmanagement;
|
||||
|
||||
import static de.jottyfan.timetrack.db.project.Tables.T_APP;
|
||||
import static de.jottyfan.timetrack.db.project.Tables.T_WORKPACKAGE_APP;
|
||||
import static de.jottyfan.timetrack.db.project.Tables.T_WORKPACKAGE;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.jooq.DSLContext;
|
||||
import org.jooq.Record7;
|
||||
import org.jooq.SelectConditionStep;
|
||||
import org.jooq.impl.DSL;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import de.jottyfan.timetrack.modules.projectmanagement.model.AppBean;
|
||||
import de.jottyfan.timetrack.modules.projectmanagement.model.WorkpackageBean;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jotty
|
||||
*/
|
||||
@Repository
|
||||
public class AppRepository {
|
||||
|
||||
@Autowired
|
||||
private DSLContext jooq;
|
||||
|
||||
/**
|
||||
* get all app beans for that workpackage
|
||||
*
|
||||
* @param fkWorkpackage the workpackage ID; if null, return all app beans
|
||||
* @return the list of app beans; an empty list at least
|
||||
*/
|
||||
public List<AppBean> getAllAppBeans(Integer fkWorkpackage) {
|
||||
SelectConditionStep<Record7<Integer, Integer, String, String, String, Integer, String>> sql = jooq
|
||||
// @formatter:off
|
||||
.select(T_APP.PK_APP,
|
||||
T_APP.FK_BUNDLE,
|
||||
T_APP.BASIC_FUNCTIONALITY,
|
||||
T_APP.NAME,
|
||||
T_APP.DESCRIPTION,
|
||||
T_APP.FK_REPLACED_BY_APP,
|
||||
T_APP.REPOSITORY_URL)
|
||||
.from(T_APP)
|
||||
.leftJoin(T_WORKPACKAGE_APP).on(T_WORKPACKAGE_APP.FK_APP.eq(T_APP.PK_APP))
|
||||
.where(fkWorkpackage == null ? DSL.trueCondition() : T_WORKPACKAGE_APP.FK_WORKPACKAGE.eq(fkWorkpackage));
|
||||
// @formatter:on
|
||||
return sql.fetchInto(AppBean.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* get the app
|
||||
*
|
||||
* @param pkApp the ID of the app
|
||||
* @return the app bean or null
|
||||
*/
|
||||
public AppBean getApp(Integer pkApp) {
|
||||
SelectConditionStep<Record7<Integer, Integer, String, String, String, Integer, String>> sql = jooq
|
||||
// @formatter:off
|
||||
.select(T_APP.PK_APP,
|
||||
T_APP.FK_BUNDLE,
|
||||
T_APP.BASIC_FUNCTIONALITY,
|
||||
T_APP.NAME,
|
||||
T_APP.DESCRIPTION,
|
||||
T_APP.FK_REPLACED_BY_APP,
|
||||
T_APP.REPOSITORY_URL)
|
||||
.from(T_APP)
|
||||
.where(T_APP.PK_APP.eq(pkApp));
|
||||
// @formatter:on
|
||||
return sql.fetchOneInto(AppBean.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* get all work packages
|
||||
*
|
||||
* @return the list; an empty list at least
|
||||
*/
|
||||
public List<WorkpackageBean> getWorkpackages() {
|
||||
return jooq.selectFrom(T_WORKPACKAGE).fetchInto(WorkpackageBean.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
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.AppBean;
|
||||
import de.jottyfan.timetrack.modules.projectmanagement.model.WorkpackageBean;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jotty
|
||||
*
|
||||
*/
|
||||
@Service
|
||||
public class AppService {
|
||||
@Autowired
|
||||
private AppRepository repository;
|
||||
|
||||
public List<AppBean> getAppsOf(Integer fkWorkpackage) {
|
||||
return repository.getAllAppBeans(fkWorkpackage);
|
||||
}
|
||||
|
||||
public AppBean getApp(Integer pkApp) {
|
||||
return repository.getApp(pkApp);
|
||||
}
|
||||
|
||||
public List<WorkpackageBean> getWorkpackages() {
|
||||
return repository.getWorkpackages();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package de.jottyfan.timetrack.modules.projectmanagement.model;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jotty
|
||||
*
|
||||
*/
|
||||
public class AppBean implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Integer pkApp;
|
||||
private Integer fkBundle;
|
||||
private String basicFunctionality;
|
||||
private String name;
|
||||
private String description;
|
||||
private Integer fkReplacedByApp;
|
||||
private String repositoryUrl;
|
||||
|
||||
/**
|
||||
* @return the pkApp
|
||||
*/
|
||||
public Integer getPkApp() {
|
||||
return pkApp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param pkApp the pkApp to set
|
||||
*/
|
||||
public void setPkApp(Integer pkApp) {
|
||||
this.pkApp = pkApp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the fkBundle
|
||||
*/
|
||||
public Integer getFkBundle() {
|
||||
return fkBundle;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fkBundle the fkBundle to set
|
||||
*/
|
||||
public void setFkBundle(Integer fkBundle) {
|
||||
this.fkBundle = fkBundle;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the basicFunctionality
|
||||
*/
|
||||
public String getBasicFunctionality() {
|
||||
return basicFunctionality;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param basicFunctionality the basicFunctionality to set
|
||||
*/
|
||||
public void setBasicFunctionality(String basicFunctionality) {
|
||||
this.basicFunctionality = basicFunctionality;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 fkReplacedByApp
|
||||
*/
|
||||
public Integer getFkReplacedByApp() {
|
||||
return fkReplacedByApp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fkReplacedByApp the fkReplacedByApp to set
|
||||
*/
|
||||
public void setFkReplacedByApp(Integer fkReplacedByApp) {
|
||||
this.fkReplacedByApp = fkReplacedByApp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the repositoryUrl
|
||||
*/
|
||||
public String getRepositoryUrl() {
|
||||
return repositoryUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param repositoryUrl the repositoryUrl to set
|
||||
*/
|
||||
public void setRepositoryUrl(String repositoryUrl) {
|
||||
this.repositoryUrl = repositoryUrl;
|
||||
}
|
||||
}
|
||||
@@ -22,3 +22,6 @@ spring.security.oauth2.client.provider.keycloak.user-name-attribute = preferred_
|
||||
# application
|
||||
server.port = ${server.port}
|
||||
server.servlet.context-path = /timetrack
|
||||
|
||||
spring.mvc.contentnegotiation.favor-parameter=false
|
||||
spring.mvc.contentnegotiation.media-types.css=text/css
|
||||
|
||||
@@ -8,19 +8,18 @@
|
||||
<link rel="stylesheet" type="text/css" th:href="@{/webjars/bootstrap/5.3.8/css/bootstrap.min.css}" />
|
||||
<link rel="stylesheet" type="text/css" th:href="@{/webjars/datatables/2.3.5/css/dataTables.dataTables.min.css}" />
|
||||
<link rel="stylesheet" type="text/css" th:href="@{/webjars/font-awesome/7.1.0/css/all.min.css}" />
|
||||
<link rel="stylesheet" type="text/css" th:href="@{/webjars/fullcalendar/6.1.10/main.css}" />
|
||||
<link rel="stylesheet" type="text/css" th:href="@{/css/style.css}">
|
||||
<link rel="stylesheet" type="text/css" th:href="@{/public/dynamicstyle.css}">
|
||||
<link rel="icon" type="image/png" sizes="32x32" th:href="@{/png/favicon/favicon-32x32.png}" />
|
||||
<link rel="icon" type="image/png" sizes="16x16" th:href="@{/png/favicon/favicon-16x16.png}" />
|
||||
|
||||
<script th:src="@{/webjars/jquery/3.7.1/jquery.min.js}"></script>
|
||||
<script th:src="@{/webjars/bootstrap/5.3.8/js/bootstrap.bundle.min.js}"></script>
|
||||
<script th:src="@{/webjars/datatables/2.3.5/js/dataTables.dataTables.min.js}"></script>
|
||||
<script th:src="@{/webjars/fullcalendar/6.1.10/main.js}"></script>
|
||||
<script th:src="@{/js/helper.js}"></script>
|
||||
<script th:src="@{/js/clock.js}"></script>
|
||||
<script th:src="@{/js/schedule.js}"></script>
|
||||
<script type="text/javascript" th:src="@{/webjars/jquery/3.7.1/jquery.min.js}"></script>
|
||||
<script type="text/javascript" th:src="@{/webjars/bootstrap/5.3.8/js/bootstrap.bundle.min.js}"></script>
|
||||
<script type="text/javascript" th:src="@{/webjars/datatables/2.3.5/js/dataTables.dataTables.min.js}"></script>
|
||||
<script type="text/javascript" th:src="@{/webjars/fullcalendar/6.1.19/index.global.min.js}"></script>
|
||||
<script type="text/javascript" th:src="@{/js/helper.js}"></script>
|
||||
<script type="text/javascript" th:src="@{/js/clock.js}"></script>
|
||||
<script type="text/javascript" th:src="@{/js/schedule.js}"></script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -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-secondary btn-white-text" th:href="@{/projectmanagement}">zur Projektübersicht</a>
|
||||
</li>
|
||||
</ul>
|
||||
<main layout:fragment="content">
|
||||
<div th:text="${app.name}"></div>
|
||||
<div th:each="p : ${workpackages}" th:text="${p.name}"></div>
|
||||
TODO: assign app to workpackage and store it
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
46
src/main/resources/templates/projectmanagement/app/list.html
Normal file
46
src/main/resources/templates/projectmanagement/app/list.html
Normal file
@@ -0,0 +1,46 @@
|
||||
<!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-secondary btn-white-text" th:href="@{/projectmanagement}">zur Projektübersicht</a>
|
||||
</li>
|
||||
</ul>
|
||||
<main layout:fragment="content">
|
||||
<div class="p-2">
|
||||
<table id="table" class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Beschreibung</th>
|
||||
<th>URL</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="b : ${apps}">
|
||||
<td th:text="${b.name}"></td>
|
||||
<td th:text="${b.description}"></td>
|
||||
<td><a th:href="${b.repositoryUrl}" target="_blank">gitlab</a></td>
|
||||
<td><a th:href="@{/projectmanagement/app/{id}/assign(id=${b.pkApp})}">zuordnen</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
var localeUrl = '[[@{/js/dataTables/de.json}]]';
|
||||
$("#table").DataTable({
|
||||
"language" : {
|
||||
"url" : localeUrl
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
@@ -16,19 +16,27 @@
|
||||
<div class="col-sm-12 col-md-3 col-lg-2 dashboardcard" th:each="p : ${projects}">
|
||||
<div class="card">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<span th:text="${p.funderName}"></span>:
|
||||
<span class="fw-bold ms-auto" th:text="${p.name}"></span>
|
||||
<span th:text="${p.funderName}"></span>: <span class="fw-bold ms-auto" th:text="${p.name}"></span>
|
||||
<a th:href="@{/projectmanagement/project/{p}/edit(p=${p.pkProject})}" class="btn btn-link ms-auto" title="bearbeiten">edit</a>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div th:each="w : ${p.workpackages}">
|
||||
<div class="d-flex justify-content-between align-items-center" th:each="w : ${p.workpackages}">
|
||||
<a th:href="@{/projectmanagement/workpackage/{id}(id=${w.pkWorkpackage})}" th:text="${w.name}"></a>
|
||||
<a th:href="@{/projectmanagement/workpackage/{id}/apps(id=${w.pkWorkpackage})}" class="ms-auto">apps</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 class="col-sm-12 col-md-3 col-lg-2 dashboardcard">
|
||||
<div class="card">
|
||||
<div class="card-header">Alles</div>
|
||||
<div class="card-body">
|
||||
<a th:href="@{/projectmanagement/apps}">alle apps</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
Reference in New Issue
Block a user