diff --git a/build.gradle b/build.gradle index cce98d4..b5494cc 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ plugins { apply plugin: 'io.spring.dependency-management' group = 'de.jottyfan' -version = '1.6.0' +version = '26.1' description = """timetrack""" @@ -24,7 +24,7 @@ repositories { } dependencies { - implementation 'de.jottyfan:timetrackjooq:20240109' + implementation 'de.jottyfan:timetrackjooq:20260114' implementation 'org.webjars:bootstrap:5.3.8' implementation 'org.webjars:font-awesome:7.1.0' diff --git a/src/main/java/de/jottyfan/timetrack/Main.java b/src/main/java/de/jottyfan/timetrack/Main.java index dfe04e1..de11795 100644 --- a/src/main/java/de/jottyfan/timetrack/Main.java +++ b/src/main/java/de/jottyfan/timetrack/Main.java @@ -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); + } } diff --git a/src/main/java/de/jottyfan/timetrack/help/MainfestRepository.java b/src/main/java/de/jottyfan/timetrack/help/MainfestRepository.java new file mode 100644 index 0000000..ab8d3c5 --- /dev/null +++ b/src/main/java/de/jottyfan/timetrack/help/MainfestRepository.java @@ -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)); + } +} diff --git a/src/main/java/de/jottyfan/timetrack/help/ManifestBean.java b/src/main/java/de/jottyfan/timetrack/help/ManifestBean.java index 02ac93d..fcd38b5 100644 --- a/src/main/java/de/jottyfan/timetrack/help/ManifestBean.java +++ b/src/main/java/de/jottyfan/timetrack/help/ManifestBean.java @@ -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 "); diff --git a/src/main/java/de/jottyfan/timetrack/modules/projectmanagement/ProjectmanagementController.java b/src/main/java/de/jottyfan/timetrack/modules/projectmanagement/ProjectmanagementController.java new file mode 100644 index 0000000..3afc4ef --- /dev/null +++ b/src/main/java/de/jottyfan/timetrack/modules/projectmanagement/ProjectmanagementController.java @@ -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"; + } +} diff --git a/src/main/java/de/jottyfan/timetrack/modules/projectmanagement/ProjectmanagementRepository.java b/src/main/java/de/jottyfan/timetrack/modules/projectmanagement/ProjectmanagementRepository.java new file mode 100644 index 0000000..cf89034 --- /dev/null +++ b/src/main/java/de/jottyfan/timetrack/modules/projectmanagement/ProjectmanagementRepository.java @@ -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 getProjects() { + List projects = jooq.selectFrom(T_PROJECT).fetchInto(TProjectRecord.class); + List workpackages = jooq.selectFrom(T_WORKPACKAGE).fetchInto(TWorkpackageRecord.class); + Map> wpMap = new HashMap<>(); + workpackages.forEach(w -> { + List found = wpMap.get(w.getFkProject()); + if (found == null) { + found = new ArrayList<>(); + wpMap.put(w.getFkProject(), found); + } + found.add(WorkpackageBean.of(w)); + }); + List 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); + } + +} diff --git a/src/main/java/de/jottyfan/timetrack/modules/projectmanagement/ProjectmanagementService.java b/src/main/java/de/jottyfan/timetrack/modules/projectmanagement/ProjectmanagementService.java new file mode 100644 index 0000000..5933978 --- /dev/null +++ b/src/main/java/de/jottyfan/timetrack/modules/projectmanagement/ProjectmanagementService.java @@ -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 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); + } +} diff --git a/src/main/java/de/jottyfan/timetrack/modules/projectmanagement/model/ProjectBean.java b/src/main/java/de/jottyfan/timetrack/modules/projectmanagement/model/ProjectBean.java new file mode 100644 index 0000000..ea05199 --- /dev/null +++ b/src/main/java/de/jottyfan/timetrack/modules/projectmanagement/model/ProjectBean.java @@ -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 workpackages; + + public ProjectBean() { + workpackages = new ArrayList<>(); + } + + public static final ProjectBean of(TProjectRecord r, List 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 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; + } +} diff --git a/src/main/java/de/jottyfan/timetrack/modules/projectmanagement/model/WorkpackageBean.java b/src/main/java/de/jottyfan/timetrack/modules/projectmanagement/model/WorkpackageBean.java new file mode 100644 index 0000000..d4c72f7 --- /dev/null +++ b/src/main/java/de/jottyfan/timetrack/modules/projectmanagement/model/WorkpackageBean.java @@ -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; + } +} diff --git a/src/main/resources/static/css/style.css b/src/main/resources/static/css/style.css index cae4444..edc8763 100644 --- a/src/main/resources/static/css/style.css +++ b/src/main/resources/static/css/style.css @@ -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; +} \ No newline at end of file diff --git a/src/main/resources/templates/layout/main.html b/src/main/resources/templates/layout/main.html index cbc8d00..2dc451c 100644 --- a/src/main/resources/templates/layout/main.html +++ b/src/main/resources/templates/layout/main.html @@ -26,6 +26,7 @@