Skip to content

Commit 20fb4d7

Browse files
authored
Improve monthly report (#273)
* Add prev/next buttons to monthly report --------- Co-authored-by: kaklakariada <kaklakariada@users.noreply.github.com>
1 parent b2e2081 commit 20fb4d7

File tree

16 files changed

+229
-67
lines changed

16 files changed

+229
-67
lines changed

.github/workflows/build.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,13 @@ jobs:
7777
./gradlew -status
7878
./gradlew -stop
7979
80+
- name: Archive test reports for ${{ runner.os }}
81+
uses: actions/upload-artifact@v4
82+
if: ${{ always() }}
83+
with:
84+
name: test-reports-${{ runner.os }}
85+
path: "**/build/reports/tests/*/**"
86+
8087
- name: Archive native package for ${{ runner.os }}
8188
uses: actions/upload-artifact@v4
8289
if: ${{ env.DEFAULT_JAVA == matrix.java }}

.vscode/settings.json

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,24 @@
55
"source.fixAll": "explicit"
66
},
77
"java.codeGeneration.useBlocks": true,
8+
"java.compile.nullAnalysis.mode": "disabled",
9+
"java.configuration.updateBuildConfiguration": "automatic",
10+
"java.test.config": {
11+
"vmArgs": [
12+
"-Djava.awt.headless=true",
13+
"-Dtestfx.headless=true",
14+
"-Dtestfx.robot=glass",
15+
"-Dglass.platform=Monocle",
16+
"-Dmonocle.platform=Headless"
17+
]
18+
},
19+
"[java]": {
20+
"editor.indentSize": 4
21+
},
822
"editor.formatOnSave": true,
923
"editor.formatOnSaveMode": "file",
1024
"sonarlint.connectedMode.project": {
1125
"connectionId": "itsallcode",
1226
"projectKey": "white-rabbit"
1327
},
14-
"java.compile.nullAnalysis.mode": "automatic",
15-
"java.configuration.updateBuildConfiguration": "automatic"
1628
}

CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,11 @@ This release requires Java 21.
2020
* [#245](https://github.com/itsallcode/white-rabbit/pull/245): Removed webstart deployment.
2121
* [#265](https://github.com/itsallcode/white-rabbit/pull/265): Upgraded dependencies, require Java 21.
2222

23-
### Changes / Bugfixes
23+
### New Features
24+
25+
* [#273](https://github.com/itsallcode/white-rabbit/pull/273): Added buttons to monthly report for jumping to the previous/next month
26+
27+
### Bugfixes
2428

2529
* [#241](https://github.com/itsallcode/white-rabbit/pull/241): Fix automatic interruption dialog popup after resume.
2630
* [#233](https://github.com/itsallcode/white-rabbit/pull/233): Upgrade dependencies, use [Gradle versions catalog](https://docs.gradle.org/current/userguide/platforms.html).

jfxui/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ task uiTest(type: Test, group: 'verification') {
124124
}
125125

126126
forkEvery 5
127-
jvmArgs '-XX:+HeapDumpOnOutOfMemoryError'
127+
jvmArgs '-XX:+HeapDumpOnOutOfMemoryError', '-XX:+EnableDynamicAgentLoading'
128128

129129
if(!project.hasProperty("uiTestsHeadless") || project.property("uiTestsHeadless") != "false") {
130130
systemProperty 'java.awt.headless', 'true'

jfxui/src/main/java/org/itsallcode/whiterabbit/jfxui/UiActions.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,18 @@
66
import java.io.UncheckedIOException;
77
import java.nio.file.Files;
88
import java.nio.file.Path;
9-
import java.util.*;
9+
import java.util.LinkedHashMap;
10+
import java.util.Map;
11+
import java.util.Optional;
1012

1113
import org.apache.logging.log4j.LogManager;
1214
import org.apache.logging.log4j.Logger;
1315
import org.itsallcode.whiterabbit.api.model.ProjectReport;
1416
import org.itsallcode.whiterabbit.jfxui.service.DesktopService;
15-
import org.itsallcode.whiterabbit.jfxui.ui.*;
17+
import org.itsallcode.whiterabbit.jfxui.ui.DailyProjectReportViewer;
18+
import org.itsallcode.whiterabbit.jfxui.ui.MonthlyProjectReportViewer;
19+
import org.itsallcode.whiterabbit.jfxui.ui.PluginManagerViewer;
20+
import org.itsallcode.whiterabbit.jfxui.ui.VacationReportViewer;
1621
import org.itsallcode.whiterabbit.logic.Config;
1722
import org.itsallcode.whiterabbit.logic.model.MonthIndex;
1823
import org.itsallcode.whiterabbit.logic.report.vacation.VacationReport;
@@ -133,8 +138,7 @@ public void showMonthlyProjectReport()
133138
LOG.warn("No month selected, can't generate project report");
134139
return;
135140
}
136-
final ProjectReport report = appService.generateProjectReport(monthIndex.getYearMonth());
137-
new MonthlyProjectReportViewer(getPrimaryStage(), state.uiState, appService, report).show();
141+
new MonthlyProjectReportViewer(getPrimaryStage(), state.uiState, appService, monthIndex.getYearMonth()).show();
138142
}
139143

140144
public void showPluginManager()

jfxui/src/main/java/org/itsallcode/whiterabbit/jfxui/table/activities/ActivityPropertyAdapter.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77

88
import org.apache.logging.log4j.LogManager;
99
import org.apache.logging.log4j.Logger;
10-
import org.eclipse.jdt.annotation.NonNull;
1110
import org.itsallcode.whiterabbit.jfxui.table.EditListener;
1211
import org.itsallcode.whiterabbit.jfxui.table.RecordPropertyAdapter;
1312
import org.itsallcode.whiterabbit.logic.model.Activity;
@@ -25,7 +24,7 @@ public final class ActivityPropertyAdapter extends RecordPropertyAdapter<Activit
2524
final ObjectProperty<Boolean> remainder;
2625
final ObjectProperty<String> comment;
2726

28-
private ActivityPropertyAdapter(EditListener<DayRecord> editListener, Activity act)
27+
private ActivityPropertyAdapter(final EditListener<DayRecord> editListener, final Activity act)
2928
{
3029
super(dayRecord -> editListener.recordUpdated(dayRecord.getDay()));
3130
setRecord(act);
@@ -36,7 +35,7 @@ private ActivityPropertyAdapter(EditListener<DayRecord> editListener, Activity a
3635
update();
3736
}
3837

39-
public void setActivity(Activity activity)
38+
public void setActivity(final Activity activity)
4039
{
4140
super.setRecord(activity);
4241
update();
@@ -50,12 +49,13 @@ public void update()
5049
});
5150
}
5251

53-
public static ActivityPropertyAdapter wrap(EditListener<DayRecord> editListener, Activity activity)
52+
public static ActivityPropertyAdapter wrap(final EditListener<DayRecord> editListener, final Activity activity)
5453
{
5554
return new ActivityPropertyAdapter(editListener, activity);
5655
}
5756

58-
static List<@NonNull ActivityPropertyAdapter> wrap(EditListener<DayRecord> editListener, List<Activity> activities)
57+
static List<ActivityPropertyAdapter> wrap(final EditListener<DayRecord> editListener,
58+
final List<Activity> activities)
5959
{
6060
return activities.stream()
6161
.map(a -> wrap(editListener, a))

jfxui/src/main/java/org/itsallcode/whiterabbit/jfxui/tray/AwtTrayIcon.java

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818

1919
import org.apache.logging.log4j.LogManager;
2020
import org.apache.logging.log4j.Logger;
21-
import org.eclipse.jdt.annotation.NonNull;
2221

2322
class AwtTrayIcon implements Tray
2423
{
@@ -27,13 +26,13 @@ class AwtTrayIcon implements Tray
2726
private final SystemTray tray;
2827
private final TrayIcon trayIcon;
2928

30-
private AwtTrayIcon(SystemTray tray, TrayIcon trayIcon)
29+
private AwtTrayIcon(final SystemTray tray, final TrayIcon trayIcon)
3130
{
3231
this.tray = tray;
3332
this.trayIcon = trayIcon;
3433
}
3534

36-
static @NonNull Tray createAwtTray(TrayCallback callback)
35+
static Tray createAwtTray(final TrayCallback callback)
3736
{
3837
try
3938
{
@@ -52,7 +51,7 @@ private AwtTrayIcon(SystemTray tray, TrayIcon trayIcon)
5251
}
5352
}
5453

55-
private static Image loadImage(final String resourceName, Dimension size)
54+
private static Image loadImage(final String resourceName, final Dimension size)
5655
{
5756
final URL imageUrl = AwtTrayIcon.class.getResource(resourceName);
5857
try
@@ -67,7 +66,7 @@ private static Image loadImage(final String resourceName, Dimension size)
6766
}
6867
}
6968

70-
private static PopupMenu createPopupMenu(TrayCallback callback)
69+
private static PopupMenu createPopupMenu(final TrayCallback callback)
7170
{
7271
final PopupMenu popupMenu = new PopupMenu("White Rabbit Time Recording");
7372
popupMenu.add(menuItem("Show", KeyEvent.VK_S, callback::showMainWindow));
@@ -77,21 +76,21 @@ private static PopupMenu createPopupMenu(TrayCallback callback)
7776
return popupMenu;
7877
}
7978

80-
private static MenuItem menuItem(String label, int shortcutKey, Runnable action)
79+
private static MenuItem menuItem(final String label, final int shortcutKey, final Runnable action)
8180
{
8281
final MenuItem menuItem = new MenuItem(label, new MenuShortcut(shortcutKey));
8382
menuItem.addActionListener(event -> action.run());
8483
return menuItem;
8584
}
8685

8786
@Override
88-
public void setTooltip(String tooltip)
87+
public void setTooltip(final String tooltip)
8988
{
9089
trayIcon.setToolTip(tooltip);
9190
}
9291

9392
@Override
94-
public void displayMessage(String caption, String text, MessageType messageType)
93+
public void displayMessage(final String caption, final String text, final MessageType messageType)
9594
{
9695
SwingUtilities.invokeLater(() -> trayIcon.displayMessage(caption, text, messageType));
9796
}

jfxui/src/main/java/org/itsallcode/whiterabbit/jfxui/ui/MonthlyProjectReportViewer.java

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
package org.itsallcode.whiterabbit.jfxui.ui;
22

33
import static java.util.stream.Collectors.joining;
4-
import static java.util.stream.Collectors.toList;
54

65
import java.time.Duration;
76
import java.time.YearMonth;
87
import java.util.List;
98

9+
import org.apache.logging.log4j.LogManager;
10+
import org.apache.logging.log4j.Logger;
1011
import org.itsallcode.whiterabbit.api.model.ProjectReport;
1112
import org.itsallcode.whiterabbit.api.model.ProjectReportActivity;
1213
import org.itsallcode.whiterabbit.jfxui.table.converter.DurationStringConverter;
@@ -19,39 +20,62 @@
1920

2021
import javafx.collections.FXCollections;
2122
import javafx.collections.ObservableList;
23+
import javafx.scene.Node;
2224
import javafx.scene.control.TableView;
2325
import javafx.stage.Stage;
2426
import javafx.util.converter.DefaultStringConverter;
2527

2628
public class MonthlyProjectReportViewer
2729
{
28-
private final ProjectReport report;
30+
private static final Logger LOG = LogManager.getLogger(MonthlyProjectReportViewer.class);
31+
2932
private final ReportWindow reportWindow;
3033
private final UiStateService uiState;
3134
private final AppService appService;
35+
private YearMonth yearMonth;
3236

33-
public MonthlyProjectReportViewer(Stage primaryStage, UiStateService uiState, AppService appService,
34-
ProjectReport report)
37+
public MonthlyProjectReportViewer(final Stage primaryStage, final UiStateService uiState,
38+
final AppService appService, final YearMonth yearMonth)
3539
{
3640
this.uiState = uiState;
3741
this.appService = appService;
38-
this.reportWindow = new ReportWindow(primaryStage, uiState, "monthly-project-report", "Monthly Project Report");
39-
this.report = report;
42+
this.yearMonth = yearMonth;
43+
this.reportWindow = new ReportWindow(primaryStage, uiState, "monthly-project-report", getWindowTitle());
4044
}
4145

4246
public void show()
4347
{
4448
final TableView<ReportRow> treeTable = createTreeTable();
45-
reportWindow.show(treeTable);
49+
updateTable(treeTable);
50+
final Node previousMonthButton = UiWidget.button("prev-month-button", "< Previous Month",
51+
e -> gotoMonth(treeTable, -1));
52+
final Node nextMonthButton = UiWidget.button("next-month-button", "Next Month >",
53+
e -> gotoMonth(treeTable, +1));
54+
reportWindow.show(treeTable, previousMonthButton, nextMonthButton);
4655
uiState.register(treeTable);
4756
}
4857

58+
private void gotoMonth(final TableView<ReportRow> treeTable, final int count)
59+
{
60+
yearMonth = yearMonth.plusMonths(count);
61+
LOG.debug("Go {} months to {}", count, yearMonth);
62+
updateTable(treeTable);
63+
reportWindow.updateTitle(getWindowTitle());
64+
}
65+
66+
private void updateTable(final TableView<ReportRow> treeTable)
67+
{
68+
treeTable.setItems(createRows());
69+
}
70+
71+
private String getWindowTitle()
72+
{
73+
return "Monthly Project Report " + yearMonth;
74+
}
75+
4976
private TableView<ReportRow> createTreeTable()
5077
{
51-
final ObservableList<ReportRow> rows = FXCollections.observableList(
52-
report.getProjects().stream()
53-
.map(project -> createRow(report.getMonth(), project)).collect(toList()));
54-
final TableView<ReportRow> treeTable = new TableView<>(rows);
78+
final TableView<ReportRow> treeTable = new TableView<>();
5579
treeTable.getColumns().addAll(List.of(
5680
UiWidget.readOnlyColumn("yearMonth", "Month",
5781
new YearMonthStringConverter(), ReportRow::getMonth),
@@ -67,7 +91,14 @@ private TableView<ReportRow> createTreeTable()
6791
return treeTable;
6892
}
6993

70-
private ReportRow createRow(YearMonth month, ProjectReportActivity project)
94+
private final ObservableList<ReportRow> createRows()
95+
{
96+
final ProjectReport report = appService.generateProjectReport(yearMonth);
97+
return FXCollections.observableList(
98+
report.getProjects().stream().map(project -> createRow(report.getMonth(), project)).toList());
99+
}
100+
101+
private ReportRow createRow(final YearMonth month, final ProjectReportActivity project)
71102
{
72103
return new ReportRow(month, project);
73104
}
@@ -77,7 +108,7 @@ public static class ReportRow
77108
private final YearMonth month;
78109
private final ProjectReportActivity project;
79110

80-
private ReportRow(YearMonth month, ProjectReportActivity project)
111+
private ReportRow(final YearMonth month, final ProjectReportActivity project)
81112
{
82113
this.month = month;
83114
this.project = project;

jfxui/src/main/java/org/itsallcode/whiterabbit/jfxui/ui/widget/ReportWindow.java

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import java.util.ArrayList;
66
import java.util.List;
77

8+
import org.apache.logging.log4j.LogManager;
9+
import org.apache.logging.log4j.Logger;
810
import org.itsallcode.whiterabbit.jfxui.ui.UiResources;
911
import org.itsallcode.whiterabbit.jfxui.ui.UiWidget;
1012
import org.itsallcode.whiterabbit.jfxui.uistate.UiStateService;
@@ -20,27 +22,30 @@
2022

2123
public class ReportWindow
2224
{
25+
private static final Logger LOG = LogManager.getLogger(ReportWindow.class);
2326
private final Stage primaryStage;
2427
private final String windowTitle;
2528
private final UiStateService uiState;
2629
private final String id;
2730
private Stage stage;
2831

29-
public ReportWindow(Stage primaryStage, UiStateService uiState, String id, String windowTitle)
32+
public ReportWindow(final Stage primaryStage, final UiStateService uiState, final String id,
33+
final String windowTitle)
3034
{
3135
this.primaryStage = primaryStage;
3236
this.uiState = uiState;
3337
this.id = id;
3438
this.windowTitle = windowTitle;
3539
}
3640

37-
public void show(Node reportView, Node... toolBarItems)
41+
public void show(final Node reportView, final Node... toolBarItems)
3842
{
3943
stage = createStage(reportView, toolBarItems);
44+
LOG.debug("Show report window");
4045
stage.show();
4146
}
4247

43-
private Stage createStage(Node reportView, Node... toolBarItems)
48+
private Stage createStage(final Node reportView, final Node... toolBarItems)
4449
{
4550
final BorderPane pane = new BorderPane();
4651
pane.setTop(createToolBar(toolBarItems));
@@ -49,7 +54,7 @@ private Stage createStage(Node reportView, Node... toolBarItems)
4954
return createStage(pane);
5055
}
5156

52-
private ToolBar createToolBar(Node... items)
57+
private ToolBar createToolBar(final Node... items)
5358
{
5459
final Node closeButton = UiWidget.button("close-button", "Close Report", e -> closeReportWindow());
5560
final List<Node> allItems = new ArrayList<>();
@@ -77,8 +82,15 @@ private Stage createStage(final Parent root)
7782
return newStage;
7883
}
7984

85+
public void updateTitle(final String title)
86+
{
87+
LOG.debug("Update window title to '{}'", title);
88+
stage.setTitle(title);
89+
}
90+
8091
private void closeReportWindow()
8192
{
93+
LOG.debug("Closing window '{}'", stage.getTitle());
8294
this.stage.close();
8395
}
8496
}

0 commit comments

Comments
 (0)