calendar like slot overview

This commit is contained in:
Jörg Henke
2024-01-05 10:42:05 +01:00
parent 568dfc8a64
commit 7fc30ffe48
6 changed files with 98 additions and 21 deletions

View File

@ -60,7 +60,8 @@ 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("slots", doneService.getSlots(day, username));
model.addAttribute("slotOffset", doneService.getSlotOffset(day));
model.addAttribute("schedule", weekBean.toJson());
model.addAttribute("recentList", doneService.getListRecent(username, 10));
model.addAttribute("projectList", doneService.getProjects(false));

View File

@ -9,9 +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.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@ -156,7 +156,7 @@ public class DoneRepository {
sql.execute();
}
public List<SlotBean> getSlots(String login) {
public Map<LocalDate, SlotBean> getSlots(LocalDate from, LocalDate until, String login) {
SelectSeekStep1<Record3<Integer, LocalDate, Integer>, LocalDate> sql = jooq
// @formatter:off
.select(T_REQUIRED_WORKTIME.PK_REQUIRED_WORKTIME,
@ -165,17 +165,21 @@ public class DoneRepository {
.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.DAY.ge(from))
.and(T_REQUIRED_WORKTIME.DAY.le(until))
.orderBy(T_REQUIRED_WORKTIME.DAY);
// @formatter:on
LOGGER.trace(sql);
Iterator<Record3<Integer, LocalDate, Integer>> i = sql.fetch().iterator();
List<SlotBean> list = new ArrayList<>();
Map<LocalDate, SlotBean> map = new HashMap<>();
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)));
LocalDate day = n.get(T_REQUIRED_WORKTIME.DAY);
Integer pk = n.get(T_REQUIRED_WORKTIME.PK_REQUIRED_WORKTIME);
Integer minutes = n.get(T_REQUIRED_WORKTIME.REQUIRED_MINUTES);
map.put(day, SlotBean.of(pk, day, minutes));
}
return list;
return map;
}
/**

View File

@ -2,8 +2,11 @@ package de.jottyfan.timetrack.modules.done;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.YearMonth;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@ -39,7 +42,7 @@ public class DoneService {
@Autowired
private TimeService timeService;
@Autowired
private DoneRepository repository;
@ -246,11 +249,42 @@ public class DoneService {
repository.upsertOvertime(bean.getId(), username, bean.getImpact(), bean.getOvertimeMinutes());
}
public List<SlotBean> getSlots(String username) {
return repository.getSlots(username);
public List<SlotBean> getSlots(LocalDate day, String username) {
YearMonth ym = YearMonth.from(day);
LocalDate from = ym.atDay(1);
LocalDate until = ym.atEndOfMonth();
Map<LocalDate, SlotBean> map = new HashMap<>();
LocalDate i = from;
while (i.isBefore(until.plusDays(1))) {
map.put(i, SlotBean.of(i));
i = i.plusDays(1);
}
map.putAll(repository.getSlots(from, until, username));
List<SlotBean> list = new ArrayList<>(map.values());
list.sort((o1, o2) -> {
return o1 == null || o2 == null || o1.getDay() == null ? 0 : o1.getDay().compareTo(o2.getDay());
});
return list;
}
public SlotBean getSlot(Integer id, String username) {
return repository.getSlot(id, username);
}
/**
* get a list of days until the 1st of the month starts in the calendar - start
* with sunday for the matrix
*
* @param day the day; only the month will be used
* @return a list of numbers
*/
public List<Integer> getSlotOffset(LocalDate day) {
List<Integer> list = new ArrayList<Integer>();
YearMonth ym = YearMonth.from(day);
LocalDate first = ym.atDay(1);
for (int i = 0; i < first.getDayOfWeek().getValue(); i++) {
list.add(i);
}
return list;
}
}

View File

@ -22,6 +22,12 @@ public class SlotBean implements Serializable {
bean.setMinutes(minutes);
return bean;
}
public static final SlotBean of(LocalDate day) {
SlotBean bean = new SlotBean();
bean.setDay(day);
return bean;
}
public String printTime() {
Integer hours = 0;
@ -74,6 +80,4 @@ public class SlotBean implements Serializable {
public void setId(Integer id) {
this.id = id;
}
}

View File

@ -406,7 +406,7 @@ body {
.slot_badge {
white-space: nowrap;
margin-bottom: 2px;
margin-bottom: 4px;
}
.slot_badge_left {
@ -415,6 +415,8 @@ body {
background-color: #ccc;
color: black;
padding-left: 2px;
padding-top: 2px;
padding-bottom: 2px;
}
[data-bs-theme=dark] .slot_badge_left {
@ -424,8 +426,8 @@ body {
.slot_badge_middle {
border-top: 1px solid silver;
border-bottom: 1px solid silver;
padding-left: 2px;
padding-right: 2px;
padding: 2px;
text-decoration: none;
}
.slot_badge_middle:hover {
@ -439,8 +441,24 @@ body {
background-color: transparent;
color: black;
padding-right: 2px;
padding-top: 2px;
padding-bottom: 2px;
}
[data-bs-theme=dark] .slot_badge_right {
color: white;
}
.flex-row-weekday {
display: flex;
flex-flow: row wrap;
}
.row-weekday > .col {
flex: 0 1 calc(100%/7);
max-width: calc(100%/7);
}
.boldy {
font-weight: bolder;
}

View File

@ -243,17 +243,33 @@
<div id="div_slot" class="tab-pane fade tab-pane-table">
<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.
Die Überstundenberechnung hängt von der Vollständigkeit der vorhandenen Slots ab; fehlen Slots, wird die Arbeitszeit jener Tage nicht eingerechnet.<br />
Hier werden nur die Slots für diesen Monat angezeigt.
</div>
<div class="container">
<div class="row">
<div class="row row-weekday">
<div class="col">Sonntag</div>
<div class="col">Montag</div>
<div class="col">Dienstag</div>
<div class="col">Mittwoch</div>
<div class="col">Donnerstag</div>
<div class="col">Freitag</div>
<div class="col">Samstag</div>
</div>
<div class="row row-weekday">
<div class="col slot_badge" th:each="o : ${slotOffset}"></div>
<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>
<span class="slot_badge_left" th:text="${#temporals.format(s.day, 'dd.MM.')}">
</span><a th:href="@{/done/slot/{id}(id=${s.id})}" class="slot_badge_middle" th:if="${s.id}">
<i class="fas fa-pencil"></i>
</a><a th:href="@{/done/slot/add?day=${d}(d=${s.day})}" class="slot_badge_middle" th:unless="${s.id}">
<i class="fas fa-plus"></i>
</a>
<span class="slot_badge_right boldy" th:text="${s.printTime()}" th:if="${s.id}"></span>
<span class="slot_badge_right" th:unless="${s.id}">&nbsp;--:--&nbsp;</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">