improvements from Maik
This commit is contained in:
@ -7,7 +7,7 @@ plugins {
|
||||
apply plugin: 'io.spring.dependency-management'
|
||||
|
||||
group = 'de.jottyfan'
|
||||
version = '1.3.4'
|
||||
version = '1.3.5'
|
||||
|
||||
description = """timetrack"""
|
||||
|
||||
@ -46,6 +46,7 @@ dependencies {
|
||||
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-web'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-test'
|
||||
implementation 'org.springframework.boot:spring-boot-devtools'
|
||||
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'
|
||||
implementation 'de.jottyfan:timetrackjooq:0.1.2'
|
||||
|
||||
|
@ -13,6 +13,7 @@ import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.SessionAttributes;
|
||||
|
||||
import de.jottyfan.timetrack.component.OAuth2Provider;
|
||||
import de.jottyfan.timetrack.modules.CommonController;
|
||||
@ -25,20 +26,26 @@ import jakarta.annotation.security.RolesAllowed;
|
||||
*
|
||||
*/
|
||||
@Controller
|
||||
@SessionAttributes("doneModel")
|
||||
public class DoneController extends CommonController {
|
||||
|
||||
@Autowired
|
||||
private OAuth2Provider provider;
|
||||
|
||||
|
||||
@Autowired
|
||||
private ProfileService profileService;
|
||||
|
||||
@Autowired
|
||||
private DoneService doneService;
|
||||
|
||||
@ModelAttribute("doneModel")
|
||||
DoneModel getdoneModel() {
|
||||
return new DoneModel();
|
||||
}
|
||||
|
||||
@RolesAllowed("timetrack_user")
|
||||
@RequestMapping(value = "/done/list")
|
||||
public String getList(@ModelAttribute DoneModel doneModel, Model model) {
|
||||
public String getList(Model model, @ModelAttribute("doneModel") DoneModel doneModel) {
|
||||
String username = provider.getName();
|
||||
Duration maxWorkTime = Duration.ofHours(8); // TODO: to the configuration file
|
||||
LocalDate day = doneModel.getDay();
|
||||
@ -47,9 +54,9 @@ public class DoneController extends CommonController {
|
||||
SummaryBean bean = new SummaryBean(list, day, maxWorkTime);
|
||||
SummaryBean weekBean = new SummaryBean(week, day, maxWorkTime);
|
||||
model.addAttribute("doneList", list);
|
||||
model.addAttribute("doneModel", doneModel);
|
||||
model.addAttribute("sum", bean);
|
||||
model.addAttribute("schedule", weekBean.toJson());
|
||||
model.addAttribute("recentList", doneService.getListRecent(username, 10));
|
||||
model.addAttribute("projectList", doneService.getProjects(false));
|
||||
model.addAttribute("moduleList", doneService.getModules(false));
|
||||
model.addAttribute("jobList", doneService.getJobs(false));
|
||||
@ -61,9 +68,7 @@ public class DoneController extends CommonController {
|
||||
@RolesAllowed("timetrack_user")
|
||||
@GetMapping("/done/abort/{day}")
|
||||
public String abort(@PathVariable String day, Model model) {
|
||||
DoneModel doneModel = new DoneModel();
|
||||
doneModel.setDayString(day);
|
||||
return getList(doneModel, model);
|
||||
return "redirect:/done/list";
|
||||
}
|
||||
|
||||
@RolesAllowed("timetrack_user")
|
||||
@ -76,10 +81,7 @@ public class DoneController extends CommonController {
|
||||
|
||||
private String toItem(DoneBean bean, Model model) {
|
||||
String username = provider.getName();
|
||||
DoneModel doneModel = new DoneModel();
|
||||
doneModel.setDay(bean.getLocalDate());
|
||||
model.addAttribute("doneBean", bean);
|
||||
model.addAttribute("doneModel", doneModel);
|
||||
model.addAttribute("projectList", doneService.getProjects(true));
|
||||
model.addAttribute("moduleList", doneService.getModules(true));
|
||||
model.addAttribute("jobList", doneService.getJobs(true));
|
||||
@ -98,23 +100,61 @@ public class DoneController extends CommonController {
|
||||
return toItem(bean, model);
|
||||
}
|
||||
|
||||
@RolesAllowed("timetrack_user")
|
||||
@GetMapping("/done/end/{id}")
|
||||
public String end(@PathVariable Integer id, Model model) {
|
||||
DoneBean bean = doneService.getBean(id);
|
||||
String username = provider.getName();
|
||||
doneService.endToNow(bean, username);
|
||||
return "redirect:/done/list";
|
||||
}
|
||||
|
||||
@RolesAllowed("timetrack_user")
|
||||
@GetMapping("/done/copy/{id}")
|
||||
public String copyFromNow(@PathVariable Integer id, Model model) {
|
||||
DoneBean bean = doneService.getBean(id);
|
||||
String username = provider.getName();
|
||||
doneService.copyFromNow(bean, username);
|
||||
return "redirect:/done/list";
|
||||
}
|
||||
|
||||
@RolesAllowed("timetrack_user")
|
||||
@RequestMapping(value = "/done/upsert", method = RequestMethod.POST)
|
||||
public String doUpsert(Model model, @ModelAttribute DoneBean bean) {
|
||||
String username = provider.getName();
|
||||
Integer amount = doneService.doUpsert(bean, username);
|
||||
DoneModel doneModel = new DoneModel();
|
||||
doneModel.setDay(bean.getLocalDate());
|
||||
return amount.equals(1) ? getList(doneModel, model) : toItem(bean.getPk(), model);
|
||||
return amount.equals(1) ? "redirect:/done/list" : "redirect:/" + toItem(bean.getPk(), model);
|
||||
}
|
||||
|
||||
@RolesAllowed("timetrack_user")
|
||||
@RequestMapping(value = "/done/addrecent/{id}", method = RequestMethod.GET)
|
||||
public String addRecent(Model model, @PathVariable Integer id) {
|
||||
String username = provider.getName();
|
||||
DoneBean bean = doneService.getBean(id);
|
||||
doneService.addRecent(bean, username);
|
||||
return "redirect:/done/list";
|
||||
}
|
||||
|
||||
@RolesAllowed("timetrack_user")
|
||||
@RequestMapping(value = "/done/list/previousday", method = RequestMethod.GET)
|
||||
public String previousDay(Model model, @ModelAttribute("doneModel") DoneModel doneModel) {
|
||||
LocalDate day = doneModel.getDay();
|
||||
doneModel.setDay(day.minusDays(1));
|
||||
return "redirect:/done/list";
|
||||
}
|
||||
|
||||
@RolesAllowed("timetrack_user")
|
||||
@RequestMapping(value = "/done/list/nextday", method = RequestMethod.GET)
|
||||
public String nextDay(Model model, @ModelAttribute("doneModel") DoneModel doneModel) {
|
||||
LocalDate day = doneModel.getDay();
|
||||
doneModel.setDay(day.plusDays(1));
|
||||
return "redirect:/done/list";
|
||||
}
|
||||
|
||||
@RolesAllowed("timetrack_user")
|
||||
@GetMapping(value = "/done/delete/{id}")
|
||||
public String doDelete(@PathVariable Integer id, Model model) {
|
||||
DoneBean bean = doneService.getBean(id);
|
||||
Integer amount = doneService.doDelete(id);
|
||||
DoneModel doneModel = new DoneModel();
|
||||
doneModel.setDay(bean.getLocalDate());
|
||||
return amount.equals(1) ? getList(doneModel, model) : toItem(id, model);
|
||||
return amount.equals(1) ? "redirect:/done/list" : "redirect:/" + toItem(id, model);
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
package de.jottyfan.timetrack.modules.done;
|
||||
|
||||
import static de.jottyfan.timetrack.db.done.Tables.T_DONE;
|
||||
import static de.jottyfan.timetrack.db.done.Tables.V_BILLING;
|
||||
import static de.jottyfan.timetrack.db.done.Tables.V_JOB;
|
||||
import static de.jottyfan.timetrack.db.done.Tables.V_MODULE;
|
||||
import static de.jottyfan.timetrack.db.done.Tables.V_PROJECT;
|
||||
@ -24,11 +23,13 @@ import org.jooq.InsertValuesStep7;
|
||||
import org.jooq.Record7;
|
||||
import org.jooq.Result;
|
||||
import org.jooq.SelectConditionStep;
|
||||
import org.jooq.SelectLimitPercentStep;
|
||||
import org.jooq.UpdateConditionStep;
|
||||
import org.jooq.exception.DataAccessException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import de.jottyfan.timetrack.db.done.tables.TDone;
|
||||
import de.jottyfan.timetrack.db.done.tables.records.TDoneRecord;
|
||||
import de.jottyfan.timetrack.db.done.tables.records.VBillingRecord;
|
||||
import de.jottyfan.timetrack.db.done.tables.records.VJobRecord;
|
||||
@ -146,7 +147,7 @@ public class DoneGateway {
|
||||
if (includeNull) {
|
||||
list.add(new VBillingRecord());
|
||||
}
|
||||
list.addAll(getJooq().selectFrom(V_BILLING).orderBy(V_BILLING.NAME).fetchInto(VBillingRecord.class));
|
||||
// list.addAll(getJooq().selectFrom(V_BILLING).orderBy(V_BILLING.NAME).fetchInto(VBillingRecord.class));
|
||||
return list;
|
||||
}
|
||||
|
||||
@ -266,6 +267,43 @@ public class DoneGateway {
|
||||
return list;
|
||||
}
|
||||
|
||||
public List<DoneBean> getRecent(Integer userId, int recentCount)
|
||||
throws DataAccessException, ClassNotFoundException, SQLException {
|
||||
TDone X = T_DONE.as("x");
|
||||
|
||||
SelectLimitPercentStep<Record7<Integer, LocalDateTime, LocalDateTime, Integer, Integer, Integer, Integer>> sql = jooq
|
||||
// @formatter:off
|
||||
.select(X.PK, X.TIME_FROM, X.TIME_UNTIL, X.FK_PROJECT, X.FK_MODULE, X.FK_JOB, X.FK_BILLING)
|
||||
.from(jooq
|
||||
.select(T_DONE.PK, T_DONE.TIME_FROM, T_DONE.TIME_UNTIL, T_DONE.FK_PROJECT, T_DONE.FK_MODULE, T_DONE.FK_JOB, T_DONE.FK_BILLING)
|
||||
.distinctOn(T_DONE.FK_PROJECT, T_DONE.FK_MODULE, T_DONE.FK_JOB)
|
||||
.from(T_DONE)
|
||||
.where(T_DONE.FK_LOGIN.eq(userId))
|
||||
.asTable(X))
|
||||
.orderBy(X.TIME_FROM.desc())
|
||||
.limit(recentCount);
|
||||
// @formatter:on
|
||||
LOGGER.trace(sql);
|
||||
List<DoneBean> list = new ArrayList<>();
|
||||
Map<Integer, VProjectRecord> projectMap = getProjectMap();
|
||||
Map<Integer, VModuleRecord> moduleMap = getModuleMap();
|
||||
Map<Integer, VJobRecord> jobMap = getJobMap();
|
||||
Map<Integer, VBillingRecord> billingMap = getBillingMap();
|
||||
for (Record7<Integer, LocalDateTime, LocalDateTime, Integer, Integer, Integer, Integer> r : sql.fetch()) {
|
||||
DoneBean bean = new DoneBean();
|
||||
bean.setPk(r.get(T_DONE.PK));
|
||||
bean.setTimeFrom(r.get(T_DONE.TIME_FROM));
|
||||
bean.setTimeUntil(r.get(T_DONE.TIME_UNTIL));
|
||||
bean.setLocalDate(bean.getLocalDate());
|
||||
bean.setProject(projectMap.get(r.get(T_DONE.FK_PROJECT)));
|
||||
bean.setModule(moduleMap.get(r.get(T_DONE.FK_MODULE)));
|
||||
bean.setActivity(jobMap.get(r.get(T_DONE.FK_JOB)));
|
||||
bean.setBilling(billingMap.get(r.get(T_DONE.FK_BILLING)));
|
||||
list.add(bean);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* get list of entries of day
|
||||
*
|
||||
|
@ -19,7 +19,7 @@ public class DoneModel implements Serializable {
|
||||
private LocalDate day;
|
||||
|
||||
public DoneModel() {
|
||||
this.day = LocalDate.now();
|
||||
day = LocalDate.now();
|
||||
}
|
||||
|
||||
public String getDayString() {
|
||||
|
@ -1,6 +1,7 @@
|
||||
package de.jottyfan.timetrack.modules.done;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@ -29,6 +30,10 @@ import de.jottyfan.timetrack.modules.note.NoteService;
|
||||
@Transactional(transactionManager = "transactionManager")
|
||||
public class DoneService {
|
||||
private static final Logger LOGGER = LogManager.getLogger(NoteService.class);
|
||||
private static final int INTERVAL = 15;
|
||||
|
||||
@Autowired
|
||||
private TimeService timeService;
|
||||
|
||||
@Autowired
|
||||
private DSLContext dsl;
|
||||
@ -142,4 +147,51 @@ public class DoneService {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public Integer endToNow(DoneBean bean, String username) {
|
||||
try {
|
||||
DoneGateway gw = new DoneGateway(dsl);
|
||||
Integer userId = gw.getUserId(username);
|
||||
bean.setTimeUntil(timeService.roundTime(LocalDateTime.now(), INTERVAL));
|
||||
return gw.upsert(bean, userId);
|
||||
} catch (Exception e) {
|
||||
LOGGER.error(e);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public Integer copyFromNow(DoneBean bean, String username) {
|
||||
try {
|
||||
DoneGateway gw = new DoneGateway(dsl);
|
||||
Integer userId = gw.getUserId(username);
|
||||
bean.setTimeFrom(timeService.roundTime(LocalDateTime.now(), INTERVAL));
|
||||
bean.setTimeUntil(null);
|
||||
bean.setPk(null);
|
||||
return gw.upsert(bean, userId);
|
||||
} catch (Exception e) {
|
||||
LOGGER.error(e);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public List<DoneBean> getListRecent(String username, int recentCount) {
|
||||
try {
|
||||
DoneGateway gw = new DoneGateway(dsl);
|
||||
Integer userId = gw.getUserId(username);
|
||||
if (userId == null) {
|
||||
LOGGER.warn("userId of user {} is null", username);
|
||||
}
|
||||
return gw.getRecent( userId, recentCount);
|
||||
} catch (Exception e) {
|
||||
LOGGER.error(e);
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
public Integer addRecent(DoneBean bean, String username) {
|
||||
bean.setPk(null);
|
||||
bean.setTimeFrom(timeService.roundTime(LocalDateTime.now(), INTERVAL));
|
||||
bean.setTimeUntil(null);
|
||||
return this.doUpsert(bean, username);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,36 @@
|
||||
package de.jottyfan.timetrack.modules.done;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jotty
|
||||
*
|
||||
*/
|
||||
@Service
|
||||
public class TimeService {
|
||||
|
||||
/**
|
||||
* calculate the next time in interval
|
||||
* @param givenTime the time given
|
||||
* @param interval the interval to round up or down to
|
||||
* @return the rounded time
|
||||
*/
|
||||
public LocalDateTime roundTime(LocalDateTime givenTime, int interval) {
|
||||
if (givenTime == null) {
|
||||
return null;
|
||||
} else {
|
||||
int minute = givenTime.getMinute();
|
||||
int compareMinute = minute % interval;
|
||||
int offset = 0;
|
||||
if (compareMinute <= (interval / 2)) {
|
||||
offset = -compareMinute;
|
||||
} else {
|
||||
offset = interval - compareMinute;
|
||||
}
|
||||
return givenTime.plusMinutes(offset);
|
||||
}
|
||||
}
|
||||
}
|
@ -50,7 +50,7 @@ public class JobController extends CommonController {
|
||||
@RequestMapping(value = "/done/upsert/job", method = RequestMethod.POST)
|
||||
public String doUpsert(Model model, @ModelAttribute TJobRecord bean) {
|
||||
Integer amount = jobService.doUpsert(bean);
|
||||
return amount.equals(1) ? doneController.getList(new DoneModel(), model) : toJob(bean.getPk(), model);
|
||||
return amount.equals(1) ? "redirect:/done/list": toJob(bean.getPk(), model);
|
||||
}
|
||||
|
||||
@RolesAllowed("timetrack_user")
|
||||
@ -63,6 +63,6 @@ public class JobController extends CommonController {
|
||||
@GetMapping(value = "/done/delete/job/{id}")
|
||||
public String doDeleteJob(@PathVariable Integer id, Model model) {
|
||||
Integer amount = jobService.doDelete(id);
|
||||
return amount.equals(1) ? doneController.getList(new DoneModel(), model) : toJob(id, model);
|
||||
return amount.equals(1) ? "redirect:/done/list" : toJob(id, model);
|
||||
}
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ public class ModuleController extends CommonController {
|
||||
@RequestMapping(value = "/done/upsert/module", method = RequestMethod.POST)
|
||||
public String doUpsert(Model model, @ModelAttribute TModuleRecord bean) {
|
||||
Integer amount = moduleService.doUpsert(bean);
|
||||
return amount.equals(1) ? doneController.getList(new DoneModel(), model) : toModule(bean.getPk(), model);
|
||||
return amount.equals(1) ? "redirect:/done/list" : toModule(bean.getPk(), model);
|
||||
}
|
||||
|
||||
@RolesAllowed("timetrack_user")
|
||||
@ -64,6 +64,6 @@ public class ModuleController extends CommonController {
|
||||
@GetMapping(value = "/done/delete/module/{id}")
|
||||
public String doDeleteModule(@PathVariable Integer id, Model model) {
|
||||
Integer amount = moduleService.doDelete(id);
|
||||
return amount.equals(1) ? doneController.getList(new DoneModel(), model) : toModule(id, model);
|
||||
return amount.equals(1) ? "redirect:/done/list" : toModule(id, model);
|
||||
}
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ public class ProjectController extends CommonController {
|
||||
@RequestMapping(value = "/done/upsert/project", method = RequestMethod.POST)
|
||||
public String doUpsert(Model model, @ModelAttribute TProjectRecord bean) {
|
||||
Integer amount = projectService.doUpsert(bean);
|
||||
return amount.equals(1) ? doneController.getList(new DoneModel(), model) : toProject(bean.getPk(), model);
|
||||
return amount.equals(1) ? "redirect:/done/list" : toProject(bean.getPk(), model);
|
||||
}
|
||||
|
||||
@RolesAllowed("timetrack_user")
|
||||
@ -63,6 +63,6 @@ public class ProjectController extends CommonController {
|
||||
@GetMapping(value = "/done/delete/project/{id}")
|
||||
public String doDeleteProject(@PathVariable Integer id, Model model) {
|
||||
Integer amount = projectService.doDelete(id);
|
||||
return amount.equals(1) ? doneController.getList(new DoneModel(), model) : toProject(id, model);
|
||||
return amount.equals(1) ? "redirect:/done/list" : toProject(id, model);
|
||||
}
|
||||
}
|
||||
|
@ -20,5 +20,5 @@ spring.security.oauth2.client.provider.keycloak.jwk-set-uri = ${keycloak.openid-
|
||||
spring.security.oauth2.client.provider.keycloak.user-name-attribute = preferred_username
|
||||
|
||||
# application
|
||||
server.port = 9001
|
||||
server.port = ${server.port}
|
||||
server.servlet.context-path = /timetrack
|
||||
|
@ -42,6 +42,13 @@ body {
|
||||
background-color: #222;
|
||||
}
|
||||
|
||||
@media(min-width:1600px) {
|
||||
.tabdivblurred {
|
||||
width: 50%;
|
||||
margin: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.tabdivblurred {
|
||||
padding: 8px;
|
||||
padding-bottom: 0px;
|
||||
|
@ -8,7 +8,8 @@
|
||||
<font layout:fragment="title">Arbeitszeit</font>
|
||||
<ul layout:fragment="menuitem">
|
||||
<li class="nav-item" sec:authorize="hasRole('timetrack_user')">
|
||||
<form th:action="@{/done/list}" th:object="${doneModel}" method="post">
|
||||
<a class="nav-link btn btn-primary btn-white-text" th:href="@{/done/list/previousday}" style="width: 50px; float: left;"><i class="fa fa-chevron-left"></i></a>
|
||||
<form th:action="@{/done/list}" th:object="${doneModel}" method="post" style="float:left;">
|
||||
<div class="nav-link" style="padding-top: 5px !important; padding-bottom: 0px !important">
|
||||
<div class="input-group input-group-sm mb-3" style="margin-bottom: 0px !important">
|
||||
<input type="date" class="form-control" th:value="*{day}" th:field="*{day}" />
|
||||
@ -16,6 +17,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<a class="nav-link btn btn-primary btn-white-text" th:href="@{/done/list/nextday}" style="width: 50px; float: left;"><i class="fa fa-chevron-right"></i></a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul layout:fragment="menu">
|
||||
@ -24,6 +26,19 @@
|
||||
<tr>
|
||||
<td><a class="nav-link btn btn-success btn-white-text" th:href="@{/done/add/{day}(day=${doneModel.day})}">Neuer
|
||||
Eintrag</a></td>
|
||||
<td>
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-white-text dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Letzte Einträge
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li th:each="recent : ${recentList}">
|
||||
<a class="dropdown-item" th:href="@{/done/addrecent/{id}(id=${recent.pk})}"
|
||||
th:text="${(recent.getJobName()!=null?recent.getJobName():'') + '@' + (recent.getProject()!=null?recent.getProject().getName():'') + (recent.getModule()!=null? ', ' + recent.getModule().getName() : '')}"></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</td>
|
||||
<td style="padding-left: 8px"><a class="nav-link btn btn-bordered btn-dangerhover" style="width: 44px" th:href="@{/done/list}"><i class="fas fa-sync"></i></a></td>
|
||||
</tr>
|
||||
</table>
|
||||
@ -54,7 +69,9 @@
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="done : ${doneList}">
|
||||
<td><a class="hoverlink" th:href="@{/done/edit/{id}(id=${done.pk})}"><span th:text="${done.timeNote}"></span></a></td>
|
||||
<td><a class="hoverlink" th:href="@{/done/edit/{id}(id=${done.pk})}"><span th:text="${done.timeNote}"></span></a>
|
||||
<a th:if="${done.timeUntil==null}" class="btn btn-outline-secondary" th:href="@{/done/end/{id}(id=${done.pk})}"><i class="fa fa-clock"></i></a>
|
||||
</td>
|
||||
<td><a class="hoverlink" th:href="@{/done/edit/{id}(id=${done.pk})}"><span th:text="${done.timeDiff}"></span></a></td>
|
||||
<td><a class="hoverlink" th:href="@{/done/edit/{id}(id=${done.pk})}"><span class="boldtext"
|
||||
th:text="${done.project?.name}"></span></a></td>
|
||||
@ -64,7 +81,9 @@
|
||||
th:text="${done.activity?.name}"></span></a></td>
|
||||
<td><span th:text="${done.billing.shortcut}" th:class="'billing ' + ${done.billing.csskey}"
|
||||
th:if="${done.billing != null}"></span></td>
|
||||
<td><a th:href="@{/done/edit/{id}(id=${done.pk})}" th:title="${done.pk}"><i class="fa fa-edit"></i></a></td>
|
||||
<td>
|
||||
<a class="" th:href="@{/done/copy/{id}(id=${done.pk})}"><i class="fa fa-copy"></i></a>
|
||||
<a style="margin-left: 5px;" th:href="@{/done/edit/{id}(id=${done.pk})}" th:title="${done.pk}"><i class="fa fa-edit"></i></a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
|
@ -0,0 +1,41 @@
|
||||
package de.jottyfan.timetrack.modules.done;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jotty
|
||||
*
|
||||
*/
|
||||
public class TestTimeService {
|
||||
|
||||
@Test
|
||||
public void testRoundTime() {
|
||||
TimeService service = new TimeService();
|
||||
LocalDateTime today = LocalDateTime.now();
|
||||
assertEquals("01:00", service.roundTime(today.withHour(1).withMinute(7), 15).format(DateTimeFormatter.ofPattern("HH:mm")));
|
||||
assertEquals("01:15", service.roundTime(today.withHour(1).withMinute(8), 15).format(DateTimeFormatter.ofPattern("HH:mm")));
|
||||
assertEquals("01:15", service.roundTime(today.withHour(1).withMinute(9), 15).format(DateTimeFormatter.ofPattern("HH:mm")));
|
||||
assertEquals("01:15", service.roundTime(today.withHour(1).withMinute(10), 15).format(DateTimeFormatter.ofPattern("HH:mm")));
|
||||
assertEquals("01:15", service.roundTime(today.withHour(1).withMinute(11), 15).format(DateTimeFormatter.ofPattern("HH:mm")));
|
||||
assertEquals("01:15", service.roundTime(today.withHour(1).withMinute(12), 15).format(DateTimeFormatter.ofPattern("HH:mm")));
|
||||
assertEquals("01:15", service.roundTime(today.withHour(1).withMinute(13), 15).format(DateTimeFormatter.ofPattern("HH:mm")));
|
||||
assertEquals("01:15", service.roundTime(today.withHour(1).withMinute(14), 15).format(DateTimeFormatter.ofPattern("HH:mm")));
|
||||
assertEquals("01:15", service.roundTime(today.withHour(1).withMinute(15), 15).format(DateTimeFormatter.ofPattern("HH:mm")));
|
||||
assertEquals("01:15", service.roundTime(today.withHour(1).withMinute(16), 15).format(DateTimeFormatter.ofPattern("HH:mm")));
|
||||
assertEquals("01:15", service.roundTime(today.withHour(1).withMinute(17), 15).format(DateTimeFormatter.ofPattern("HH:mm")));
|
||||
assertEquals("01:15", service.roundTime(today.withHour(1).withMinute(18), 15).format(DateTimeFormatter.ofPattern("HH:mm")));
|
||||
assertEquals("01:15", service.roundTime(today.withHour(1).withMinute(19), 15).format(DateTimeFormatter.ofPattern("HH:mm")));
|
||||
assertEquals("01:15", service.roundTime(today.withHour(1).withMinute(20), 15).format(DateTimeFormatter.ofPattern("HH:mm")));
|
||||
assertEquals("01:15", service.roundTime(today.withHour(1).withMinute(21), 15).format(DateTimeFormatter.ofPattern("HH:mm")));
|
||||
assertEquals("01:15", service.roundTime(today.withHour(1).withMinute(22), 15).format(DateTimeFormatter.ofPattern("HH:mm")));
|
||||
assertEquals("01:30", service.roundTime(today.withHour(1).withMinute(23), 15).format(DateTimeFormatter.ofPattern("HH:mm")));
|
||||
assertEquals("01:45", service.roundTime(today.withHour(1).withMinute(52), 15).format(DateTimeFormatter.ofPattern("HH:mm")));
|
||||
assertEquals("02:00", service.roundTime(today.withHour(1).withMinute(53), 15).format(DateTimeFormatter.ofPattern("HH:mm")));
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user