diff --git a/.gitea/workflows/restart.yaml b/.gitea/workflows/restart.yaml new file mode 100644 index 0000000..fe99f88 --- /dev/null +++ b/.gitea/workflows/restart.yaml @@ -0,0 +1,14 @@ +name: Docker Restart Manuell + +on: + workflow_dispatch: + +jobs: + restart-docker: + runs-on: self-hosted + steps: + - name: Docker Compose Restart + run: | + cd /pfad/zum/docker-verzeichnis + docker compose down + docker compose up -d \ No newline at end of file diff --git a/src/main/java/de/nilzbu/mytimetracker/repository/TimeEntryRepository.java b/src/main/java/de/nilzbu/mytimetracker/repository/TimeEntryRepository.java index 3e9ca1d..3a78d62 100644 --- a/src/main/java/de/nilzbu/mytimetracker/repository/TimeEntryRepository.java +++ b/src/main/java/de/nilzbu/mytimetracker/repository/TimeEntryRepository.java @@ -12,10 +12,6 @@ public interface TimeEntryRepository extends JpaRepository { List findAllByUserOrderByDateDesc(User user); - Optional findTopByUserOrderByDateDesc(User user); - - boolean existsByUserAndDate(User user, LocalDate date); - Optional findByUserAndDate(User user, LocalDate date); List findByUserAndDateBetween(User user, LocalDate start, LocalDate end); diff --git a/src/main/java/de/nilzbu/mytimetracker/service/TimeEntryService.java b/src/main/java/de/nilzbu/mytimetracker/service/TimeEntryService.java index a7dc852..11efcde 100644 --- a/src/main/java/de/nilzbu/mytimetracker/service/TimeEntryService.java +++ b/src/main/java/de/nilzbu/mytimetracker/service/TimeEntryService.java @@ -32,7 +32,7 @@ public class TimeEntryService { return 0; } long total = Duration.between(entry.getStartTime(), entry.getEndTime()).toMinutes(); - return (int)(total - entry.getPauseMinutes()); + return (int) (total - entry.getPauseMinutes()); } public long calculateDeviation(TimeEntry entry) { @@ -49,6 +49,10 @@ public class TimeEntryService { return repository.findByUserAndDateBetween(user, start, end); } + public int getNumberOfEntriesForMonth(User user, int year, int month) { + return getEntriesForMonth(user, year, month).size(); + } + public List getEntriesForQuarter(User user, int year, int quarter) { int startMonth = (quarter - 1) * 3 + 1; LocalDate start = LocalDate.of(year, startMonth, 1); @@ -56,12 +60,20 @@ public class TimeEntryService { return repository.findByUserAndDateBetween(user, start, end); } + public int getNumberOfEntriesForQuarter(User user, int year, int month) { + return getEntriesForQuarter(user, year, month).size(); + } + public List getEntriesForYear(User user, int year) { LocalDate start = LocalDate.of(year, 1, 1); LocalDate end = LocalDate.of(year, 12, 31); return repository.findByUserAndDateBetween(user, start, end); } + public int getNumberOfEntriesForYear(User user, int year) { + return getEntriesForYear(user, year).size(); + } + public Optional getEarliestEntryDate(User user) { return repository.findFirstByUserOrderByDateAsc(user) .map(TimeEntry::getDate); diff --git a/src/main/java/de/nilzbu/mytimetracker/ui/layout/MainLayout.java b/src/main/java/de/nilzbu/mytimetracker/ui/layout/MainLayout.java index 6897b22..573f174 100644 --- a/src/main/java/de/nilzbu/mytimetracker/ui/layout/MainLayout.java +++ b/src/main/java/de/nilzbu/mytimetracker/ui/layout/MainLayout.java @@ -8,7 +8,7 @@ import com.vaadin.flow.component.orderedlayout.HorizontalLayout; import com.vaadin.flow.router.RouterLink; import com.vaadin.flow.server.VaadinServletRequest; import com.vaadin.flow.server.VaadinServletResponse; -import de.nilzbu.mytimetracker.ui.view.MainView; +import de.nilzbu.mytimetracker.ui.view.DashboardOverView; import de.nilzbu.mytimetracker.ui.view.TimeEntryView; import de.nilzbu.mytimetracker.ui.view.UserManagementView; import jakarta.annotation.security.PermitAll; @@ -31,7 +31,7 @@ public class MainLayout extends AppLayout { .set("font-size", "var(--lumo-font-size-l)") .set("margin", "0"); - RouterLink dashboardLink = new RouterLink("Dashboard", MainView.class); + RouterLink dashboardLink = new RouterLink("Dashboard", DashboardOverView.class); RouterLink bookingsLink = new RouterLink("Bookings", TimeEntryView.class); RouterLink adminLink = new RouterLink("Admin", UserManagementView.class); diff --git a/src/main/java/de/nilzbu/mytimetracker/ui/view/MainView.java b/src/main/java/de/nilzbu/mytimetracker/ui/view/DashboardOverView.java similarity index 76% rename from src/main/java/de/nilzbu/mytimetracker/ui/view/MainView.java rename to src/main/java/de/nilzbu/mytimetracker/ui/view/DashboardOverView.java index 771b7b8..689b12d 100644 --- a/src/main/java/de/nilzbu/mytimetracker/ui/view/MainView.java +++ b/src/main/java/de/nilzbu/mytimetracker/ui/view/DashboardOverView.java @@ -4,29 +4,33 @@ import com.vaadin.flow.component.Component; import com.vaadin.flow.component.combobox.ComboBox; import com.vaadin.flow.component.html.Div; import com.vaadin.flow.component.html.H2; +import com.vaadin.flow.component.orderedlayout.HorizontalLayout; import com.vaadin.flow.component.orderedlayout.VerticalLayout; import com.vaadin.flow.component.tabs.Tab; import com.vaadin.flow.component.tabs.Tabs; import com.vaadin.flow.router.PageTitle; import com.vaadin.flow.router.Route; +import de.nilzbu.mytimetracker.model.DayStatus; import de.nilzbu.mytimetracker.model.TimeEntry; import de.nilzbu.mytimetracker.model.User; import de.nilzbu.mytimetracker.repository.UserRepository; import de.nilzbu.mytimetracker.service.TimeEntryService; import de.nilzbu.mytimetracker.ui.component.ChartJsComponent; import de.nilzbu.mytimetracker.ui.layout.MainLayout; +import de.nilzbu.mytimetracker.ui.widget.KeyFigureWidget; import jakarta.annotation.security.PermitAll; import org.springframework.security.core.context.SecurityContextHolder; import java.time.LocalDate; import java.util.*; +import java.util.function.BiFunction; import java.util.stream.Collectors; import java.util.stream.IntStream; @PermitAll @PageTitle("Dashboard") @Route(value = "", layout = MainLayout.class) -public class MainView extends VerticalLayout { +public class DashboardOverView extends VerticalLayout { private final TimeEntryService timeEntryService; private final UserRepository userRepository; @@ -39,7 +43,10 @@ public class MainView extends VerticalLayout { private final Div filterContainer = new Div(); private final Div contentContainer = new Div(); - public MainView(TimeEntryService timeEntryService, UserRepository userRepository) { + private final BiFunction, DayStatus, String> calculateDaysWithDayStatus = + (scopedEntries, status) -> "%d".formatted(scopedEntries.stream().filter(entry -> entry.getStatus().equals(status)).count()); + + public DashboardOverView(TimeEntryService timeEntryService, UserRepository userRepository) { this.timeEntryService = timeEntryService; this.userRepository = userRepository; @@ -50,13 +57,11 @@ public class MainView extends VerticalLayout { } private void configureLayout() { - setSizeFull(); setPadding(false); setSpacing(false); setMargin(false); contentContainer.setSizeFull(); - contentContainer.getStyle().set("overflow", "auto"); setFlexGrow(1, contentContainer); } @@ -107,7 +112,7 @@ public class MainView extends VerticalLayout { List entries = switch (scope) { case "Month" -> { - configureFilterScope(yearSelector, monthSelector ); + configureFilterScope(yearSelector, monthSelector); yield timeEntryService.getEntriesForMonth(currentUser, yearSelector.getValue(), monthSelector.getValue()); } case "Quarter" -> { @@ -121,6 +126,7 @@ public class MainView extends VerticalLayout { default -> timeEntryService.getEntriesForUser(currentUser); }; + renderKeyFigures(entries); renderCharts(entries); } @@ -139,16 +145,13 @@ public class MainView extends VerticalLayout { private void renderCharts(List scopedEntries) { List allEntries = timeEntryService.getEntriesForUser(currentUser); - VerticalLayout chartLayout = new VerticalLayout(); - chartLayout.setSizeFull(); + HorizontalLayout chartLayout = new HorizontalLayout(); chartLayout.setPadding(false); chartLayout.setSpacing(true); Component categoryChart = createCategoryBarChart(scopedEntries); Component overtimeChart = createOvertimeLineChart(allEntries, scopedEntries); - categoryChart.getStyle().set("minHeight", "45vh"); - overtimeChart.getStyle().set("minHeight", "50vh"); chartLayout.add(categoryChart, overtimeChart); chartLayout.setFlexGrow(1, categoryChart); @@ -157,6 +160,42 @@ public class MainView extends VerticalLayout { contentContainer.add(chartLayout); } + private void renderKeyFigures(List scopedEntries) { + HorizontalLayout keyFigureLayout = new HorizontalLayout(); + + KeyFigureWidget workingDays = new KeyFigureWidget("Working Days", "" + scopedEntries.size()); + + KeyFigureWidget remoteDays = new KeyFigureWidget( + "Remote Days", calculateDaysWithDayStatus.apply(scopedEntries, DayStatus.REMOTE) + ); + + KeyFigureWidget officeDays = new KeyFigureWidget( + "Office Days", + calculateDaysWithDayStatus.apply(scopedEntries, DayStatus.OFFICE) + ); + + KeyFigureWidget vacationDays = new KeyFigureWidget( + "Vacation Days", + calculateDaysWithDayStatus.apply(scopedEntries, DayStatus.VACATION) + ); + + KeyFigureWidget sickDays = new KeyFigureWidget( + "Sick Days", + calculateDaysWithDayStatus.apply(scopedEntries, DayStatus.SICK) + ); + + KeyFigureWidget deviation = new KeyFigureWidget( + "Deviation", + "" + scopedEntries.stream() + .map(entry -> timeEntryService.calculateNetWorkMinutes(entry) - entry.getTargetMinutes()) + .reduce(0, Integer::sum) + ); + + keyFigureLayout.add(deviation, workingDays, remoteDays, officeDays, vacationDays, sickDays); + + contentContainer.add(keyFigureLayout); + } + private Component createCategoryBarChart(List entries) { Map statusCount = entries.stream() .collect(Collectors.groupingBy(e -> e.getStatus().name(), Collectors.counting())); @@ -191,6 +230,7 @@ public class MainView extends VerticalLayout { return new ChartJsComponent( ChartJsComponent.generateLineChartData(scopedDates, saldoValues), - "Overtime Balance Over Time (in hours)"); + "Overtime Balance Over Time (in hours)" + ); } } \ No newline at end of file diff --git a/src/main/java/de/nilzbu/mytimetracker/ui/widget/KeyFigureWidget.java b/src/main/java/de/nilzbu/mytimetracker/ui/widget/KeyFigureWidget.java new file mode 100644 index 0000000..2f1a535 --- /dev/null +++ b/src/main/java/de/nilzbu/mytimetracker/ui/widget/KeyFigureWidget.java @@ -0,0 +1,15 @@ +package de.nilzbu.mytimetracker.ui.widget; + +import com.vaadin.flow.component.html.NativeLabel; +import com.vaadin.flow.component.orderedlayout.VerticalLayout; + +public class KeyFigureWidget extends VerticalLayout { + + public KeyFigureWidget(String name, String value) { + NativeLabel nameLabel = new NativeLabel(name + ":"); + NativeLabel valueLabel = new NativeLabel(value); + valueLabel.getStyle().set("font-weight", "bold"); + add(nameLabel, valueLabel); + } +} +