slots basic info
This commit is contained in:
@ -60,6 +60,7 @@ public class DoneController extends CommonController {
|
||||
model.addAttribute("sum", sumBean);
|
||||
model.addAttribute("daysum", doneService.getDaysum(day, username));
|
||||
model.addAttribute("overtimeBean", doneService.getOvertimeBean(username));
|
||||
model.addAttribute("slots", doneService.getSlots(username));
|
||||
model.addAttribute("schedule", weekBean.toJson());
|
||||
model.addAttribute("recentList", doneService.getListRecent(username, 10));
|
||||
model.addAttribute("projectList", doneService.getProjects(false));
|
||||
@ -200,4 +201,12 @@ public class DoneController extends CommonController {
|
||||
doneService.upsertOvertime(bean, username);
|
||||
return "redirect:/done/list";
|
||||
}
|
||||
|
||||
@RolesAllowed("timetrack_user")
|
||||
@GetMapping("/done/slot/{id}")
|
||||
public String loadSlot(@PathVariable("id") Integer id, Model model) {
|
||||
String username = provider.getName();
|
||||
model.addAttribute("bean", doneService.getSlot(id, username));
|
||||
return "/done/slot/item";
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,9 @@ import java.time.Duration;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
@ -21,6 +24,7 @@ import org.jooq.Record3;
|
||||
import org.jooq.Record5;
|
||||
import org.jooq.SelectConditionStep;
|
||||
import org.jooq.SelectHavingStep;
|
||||
import org.jooq.SelectSeekStep1;
|
||||
import org.jooq.UpdateConditionStep;
|
||||
import org.jooq.impl.DSL;
|
||||
import org.jooq.types.YearToSecond;
|
||||
@ -30,6 +34,7 @@ import org.springframework.stereotype.Repository;
|
||||
import de.jottyfan.timetrack.db.done.tables.records.TOvertimeRecord;
|
||||
import de.jottyfan.timetrack.modules.done.model.DaysumBean;
|
||||
import de.jottyfan.timetrack.modules.done.model.OvertimeBean;
|
||||
import de.jottyfan.timetrack.modules.done.model.SlotBean;
|
||||
|
||||
/**
|
||||
*
|
||||
@ -46,7 +51,7 @@ public class DoneRepository {
|
||||
public DaysumBean getDaysum(LocalDate day, String login) {
|
||||
Field<LocalTime> WORKTIME = DSL.field("worktime", LocalTime.class);
|
||||
Field<LocalTime> BREAKTIME = DSL.field("breaktime", LocalTime.class);
|
||||
|
||||
|
||||
SelectConditionStep<Record5<LocalTime, LocalTime, LocalTime, LocalTime, YearToSecond>> sql = jooq
|
||||
// @formatter:off
|
||||
.select(V_DAY.STARTTIME,
|
||||
@ -71,12 +76,13 @@ public class DoneRepository {
|
||||
bean.setBreaks(r.get(BREAKTIME));
|
||||
YearToSecond dayOvertime = r.get(V_DAY.DAY_OVERTIME);
|
||||
Duration dayOvertimeDuration = dayOvertime == null ? null : dayOvertime.toDuration();
|
||||
bean.setDayOvertime(dayOvertimeDuration == null ? null : Long.valueOf(dayOvertimeDuration.toMinutes()).intValue());
|
||||
bean.setDayOvertime(
|
||||
dayOvertimeDuration == null ? null : Long.valueOf(dayOvertimeDuration.toMinutes()).intValue());
|
||||
bean.setTotalOvertime(getOvertimeOf(day, login));
|
||||
return bean;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private Integer getOvertimeOf(LocalDate day, String login) {
|
||||
Field<Integer> OVERTIME = DSL.field("overtime", Integer.class);
|
||||
SelectHavingStep<Record1<Integer>> sql = jooq
|
||||
@ -149,4 +155,51 @@ public class DoneRepository {
|
||||
LOGGER.trace(sql);
|
||||
sql.execute();
|
||||
}
|
||||
|
||||
public List<SlotBean> getSlots(String login) {
|
||||
SelectSeekStep1<Record3<Integer, LocalDate, Integer>, LocalDate> sql = jooq
|
||||
// @formatter:off
|
||||
.select(T_REQUIRED_WORKTIME.PK_REQUIRED_WORKTIME,
|
||||
T_REQUIRED_WORKTIME.DAY,
|
||||
T_REQUIRED_WORKTIME.REQUIRED_MINUTES)
|
||||
.from(T_REQUIRED_WORKTIME)
|
||||
.innerJoin(T_LOGIN).on(T_LOGIN.PK.eq(T_REQUIRED_WORKTIME.FK_LOGIN))
|
||||
.where(T_LOGIN.LOGIN.eq(login))
|
||||
.orderBy(T_REQUIRED_WORKTIME.DAY);
|
||||
// @formatter:on
|
||||
LOGGER.trace(sql);
|
||||
Iterator<Record3<Integer, LocalDate, Integer>> i = sql.fetch().iterator();
|
||||
List<SlotBean> list = new ArrayList<>();
|
||||
while (i.hasNext()) {
|
||||
Record3<Integer, LocalDate, Integer> n = i.next();
|
||||
list.add(SlotBean.of(n.get(T_REQUIRED_WORKTIME.PK_REQUIRED_WORKTIME), n.get(T_REQUIRED_WORKTIME.DAY),
|
||||
n.get(T_REQUIRED_WORKTIME.REQUIRED_MINUTES)));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* get slot if login fits
|
||||
*
|
||||
* @param id the ID of the slot
|
||||
* @param login the login
|
||||
* @return the slot or null
|
||||
*/
|
||||
public SlotBean getSlot(Integer id, String login) {
|
||||
SelectConditionStep<Record3<Integer, LocalDate, Integer>> sql = jooq
|
||||
// @formatter:off
|
||||
.select(T_REQUIRED_WORKTIME.PK_REQUIRED_WORKTIME,
|
||||
T_REQUIRED_WORKTIME.DAY,
|
||||
T_REQUIRED_WORKTIME.REQUIRED_MINUTES)
|
||||
.from(T_REQUIRED_WORKTIME)
|
||||
.innerJoin(T_LOGIN).on(T_LOGIN.PK.eq(T_REQUIRED_WORKTIME.FK_LOGIN))
|
||||
.where(T_LOGIN.LOGIN.eq(login))
|
||||
.and(T_REQUIRED_WORKTIME.PK_REQUIRED_WORKTIME.eq(id));
|
||||
// @formatter:on
|
||||
LOGGER.trace(sql);
|
||||
Record3<Integer, LocalDate, Integer> r = sql.fetchOne();
|
||||
return r == null ? null
|
||||
: SlotBean.of(r.get(T_REQUIRED_WORKTIME.PK_REQUIRED_WORKTIME), r.get(T_REQUIRED_WORKTIME.DAY),
|
||||
r.get(T_REQUIRED_WORKTIME.REQUIRED_MINUTES));
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ import de.jottyfan.timetrack.modules.done.model.DaysumBean;
|
||||
import de.jottyfan.timetrack.modules.done.model.DoneBean;
|
||||
import de.jottyfan.timetrack.modules.done.model.FavoriteBean;
|
||||
import de.jottyfan.timetrack.modules.done.model.OvertimeBean;
|
||||
import de.jottyfan.timetrack.modules.done.model.SlotBean;
|
||||
import de.jottyfan.timetrack.modules.note.NoteService;
|
||||
|
||||
/**
|
||||
@ -244,4 +245,12 @@ public class DoneService {
|
||||
public void upsertOvertime(OvertimeBean bean, String username) {
|
||||
repository.upsertOvertime(bean.getId(), username, bean.getImpact(), bean.getOvertimeMinutes());
|
||||
}
|
||||
|
||||
public List<SlotBean> getSlots(String username) {
|
||||
return repository.getSlots(username);
|
||||
}
|
||||
|
||||
public SlotBean getSlot(Integer id, String username) {
|
||||
return repository.getSlot(id, username);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,79 @@
|
||||
package de.jottyfan.timetrack.modules.done.model;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDate;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jotty
|
||||
*
|
||||
*/
|
||||
public class SlotBean implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Integer id;
|
||||
private LocalDate day;
|
||||
private Integer minutes;
|
||||
|
||||
public static final SlotBean of(Integer id, LocalDate day, Integer minutes) {
|
||||
SlotBean bean = new SlotBean();
|
||||
bean.setId(id);
|
||||
bean.setDay(day);
|
||||
bean.setMinutes(minutes);
|
||||
return bean;
|
||||
}
|
||||
|
||||
public String printTime() {
|
||||
Integer hours = 0;
|
||||
Integer mins = 0;
|
||||
if (minutes != null) {
|
||||
hours = minutes / 60;
|
||||
mins = minutes % 60;
|
||||
}
|
||||
return String.format("%2d:%02d", hours, mins);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the day
|
||||
*/
|
||||
public LocalDate getDay() {
|
||||
return day;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param day the day to set
|
||||
*/
|
||||
public void setDay(LocalDate day) {
|
||||
this.day = day;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the minutes
|
||||
*/
|
||||
public Integer getMinutes() {
|
||||
return minutes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param minutes the minutes to set
|
||||
*/
|
||||
public void setMinutes(Integer minutes) {
|
||||
this.minutes = minutes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the id
|
||||
*/
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param id the id to set
|
||||
*/
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -402,4 +402,45 @@ body {
|
||||
|
||||
.golden {
|
||||
color: darkgoldenrod;
|
||||
}
|
||||
}
|
||||
|
||||
.slot_badge {
|
||||
white-space: nowrap;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.slot_badge_left {
|
||||
border: 1px solid silver;
|
||||
border-radius: 12px 0px 0px 12px;
|
||||
background-color: #ccc;
|
||||
color: black;
|
||||
padding-left: 2px;
|
||||
}
|
||||
|
||||
[data-bs-theme=dark] .slot_badge_left {
|
||||
background-color: gray;
|
||||
}
|
||||
|
||||
.slot_badge_middle {
|
||||
border-top: 1px solid silver;
|
||||
border-bottom: 1px solid silver;
|
||||
padding-left: 2px;
|
||||
padding-right: 2px;
|
||||
}
|
||||
|
||||
.slot_badge_middle:hover {
|
||||
color: white;
|
||||
background-image: linear-gradient(to right bottom, #99c1f1, #1a5f64);
|
||||
}
|
||||
|
||||
.slot_badge_right {
|
||||
border: 1px solid silver;
|
||||
border-radius: 0px 12px 12px 0px;
|
||||
background-color: transparent;
|
||||
color: black;
|
||||
padding-right: 2px;
|
||||
}
|
||||
|
||||
[data-bs-theme=dark] .slot_badge_right {
|
||||
color: white;
|
||||
}
|
||||
|
@ -244,7 +244,16 @@
|
||||
<div class="alert alert-info">
|
||||
Zur Berechnung der täglichen Überstunden müssen Slots angelegt werden, die definieren, an welchen Tagen wieviele Stunden zu arbeiten ist.
|
||||
Urlaub und Arbeitsbefreiung können durch das Entfernen des jeweiligen Slots ermöglicht werden.
|
||||
Die Überstundenberechnung hängt von der Vollständigkeit der vorhandenen Slots ab; fehlen Slots, wird die Arbeitszeit jener Tage nicht eingerechnet.
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col slot_badge" th:each="s : ${slots}">
|
||||
<span class="slot_badge_left" th:text="${#temporals.format(s.day, 'EEE, dd.MM.yyyy')}"></span><a th:href="@{/done/slot/{id}(id=${s.id})}" class="slot_badge_middle"><i class="fas fa-pencil"></i></a><span class="slot_badge_right" th:text="${s.printTime()}"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
TODO: nur die Slots dieses Monats anzeigen, damit die Ladezeit nicht unnötig belastet wird
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
|
23
src/main/resources/templates/done/slot/item.html
Normal file
23
src/main/resources/templates/done/slot/item.html
Normal file
@ -0,0 +1,23 @@
|
||||
<!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>Slot aktualisieren</title>
|
||||
</head>
|
||||
<body>
|
||||
<ul layout:fragment="menu">
|
||||
</ul>
|
||||
<main layout:fragment="content">
|
||||
<div class="container formpane">
|
||||
<div class="row" th:if="${bean}">
|
||||
<div class="col-sm-3">ID</div>
|
||||
<div class="col-sm-9" th:text="${bean.id}"></div>
|
||||
<div class="col-sm-3">Tag</div>
|
||||
<div class="col-sm-9" th:text="${#temporals.format(bean.day, 'EEEE, dd.MM.yyyy')}"></div>
|
||||
<div class="col-sm-3">vereinbarte Arbeitszeit</div>
|
||||
<div class="col-sm-9" th:text="${bean.printTime()}"></div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
Reference in New Issue
Block a user