Skip to content

Commit 84fb254

Browse files
committed
#178: close map projects dialog on button. allow project creation in mapping. remove removed mappings.
1 parent 9283c66 commit 84fb254

File tree

5 files changed

+137
-44
lines changed

5 files changed

+137
-44
lines changed

src/main/java/de/doubleslash/keeptime/controller/Controller.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ public Work saveCurrentWork(final LocalDateTime workEnd) {
111111
return model.getWorkRepository().save(currentWork);
112112
}
113113

114-
public void addNewProject(final Project project) {
114+
public Project addNewProject(final Project project) {
115115
LOG.info("Creating new project '{}'.", project);
116116

117117
model.getAllProjects().add(project);
@@ -121,6 +121,7 @@ public void addNewProject(final Project project) {
121121
model.getAvailableProjects().size(), project.getIndex());
122122
changedProjects.add(project);
123123
model.getProjectRepository().saveAll(changedProjects);
124+
return project;
124125
}
125126

126127

src/main/java/de/doubleslash/keeptime/view/ManageProjectController.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,10 @@ public ManageProjectController(final Model model) {
6666
}
6767
@FXML
6868
private void initialize() {
69-
final int availableProjectAmount = model.getAllProjects().size();
69+
final int availableProjectAmount = model.getAvailableProjects().size();
7070
sortIndexSpinner
7171
.setValueFactory(new IntegerSpinnerValueFactory(0, availableProjectAmount, availableProjectAmount));
72-
sortIndexSpinner.getValueFactory().setValue(model.getAvailableProjects().size());
72+
sortIndexSpinner.getValueFactory().setValue(availableProjectAmount);
7373
formValidProperty.bind(Bindings.createBooleanBinding(() -> !nameTextField.getText().isBlank(),nameTextField.textProperty()));
7474
validateTextAlert.visibleProperty().bind(formValidProperty.not());
7575

src/main/java/de/doubleslash/keeptime/view/MapExternalProjectsController.java

Lines changed: 126 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package de.doubleslash.keeptime.view;
1818

19+
import de.doubleslash.keeptime.controller.Controller;
1920
import de.doubleslash.keeptime.model.ExternalProjectMapping;
2021
import de.doubleslash.keeptime.model.ExternalSystem;
2122
import de.doubleslash.keeptime.model.Model;
@@ -27,13 +28,17 @@
2728
import javafx.beans.property.SimpleObjectProperty;
2829
import javafx.beans.property.SimpleStringProperty;
2930
import javafx.collections.FXCollections;
31+
import javafx.collections.ObservableList;
3032
import javafx.collections.transformation.FilteredList;
3133
import javafx.fxml.FXML;
3234
import javafx.scene.control.*;
35+
import javafx.scene.paint.Color;
36+
import javafx.stage.Stage;
3337
import org.slf4j.Logger;
3438
import org.slf4j.LoggerFactory;
3539
import org.springframework.stereotype.Component;
3640

41+
import java.time.LocalDate;
3742
import java.util.List;
3843
import java.util.Optional;
3944

@@ -43,9 +48,12 @@ public class MapExternalProjectsController {
4348
private static final Logger LOG = LoggerFactory.getLogger(MapExternalProjectsController.class);
4449

4550
private final Model model;
51+
private final Controller controller;
4652
private final HeimatSettings heimatSettings;
4753
private final ExternalProjectsMappingsRepository externalProjectsMappingsRepository;
4854

55+
private Stage thisStage;
56+
4957
@FXML
5058
private TableView<ProjectMapping> mappingTableView;
5159

@@ -58,23 +66,36 @@ public class MapExternalProjectsController {
5866
@FXML
5967
private CheckBox filterOnlyWorkCheckBox;
6068

61-
// TODO maybe implement for edge case where user wants a different date than today
62-
// but what happens with mapped projects not existing at that date? but actually not related to this feature alone
63-
// @FXML
64-
private DatePicker datePicker;
69+
@FXML
70+
private ComboBox<HeimatTask> addNewProjectComboBox;
71+
72+
@FXML
73+
private Button addNewProjectButton;
74+
75+
@FXML
76+
private DatePicker tasksForDateDatePicker;
6577

66-
public MapExternalProjectsController(final Model model, HeimatSettings heimatSettings,
78+
public MapExternalProjectsController(final Model model, Controller controller, HeimatSettings heimatSettings,
6779
ExternalProjectsMappingsRepository externalProjectsMappingsRepository) {
6880
this.model = model;
81+
this.controller = controller;
6982
this.heimatSettings = heimatSettings;
7083
this.externalProjectsMappingsRepository = externalProjectsMappingsRepository;
7184
}
7285

86+
public void setStage(final Stage thisStage) {
87+
this.thisStage = thisStage;
88+
}
89+
7390
@FXML
7491
private void initialize() {
92+
tasksForDateDatePicker.setValue(LocalDate.now());
93+
tasksForDateDatePicker.setDisable(true);
94+
// TODO add listener on this thing
95+
// but what happens with mapped projects not existing at that date? but actually not related to this feature alone
7596

7697
final HeimatAPI heimatAPI = new HeimatAPI(heimatSettings.getHeimatUrl(), heimatSettings.getHeimatPat());
77-
final List<HeimatTask> externalProjects = heimatAPI.getMyTasks();
98+
final List<HeimatTask> externalProjects = heimatAPI.getMyTasks(tasksForDateDatePicker.getValue());
7899

79100
final List<ExternalProjectMapping> alreadyMappedProjects = externalProjectsMappingsRepository.findByExternalSystemId(
80101
ExternalSystem.Heimat);
@@ -92,20 +113,22 @@ private void initialize() {
92113
.findAny();
93114
if (any.isEmpty()) {
94115
LOG.warn("A mapping exists but task does not exist anymore in HEIMAT! {}.", mapping.get());
116+
// TODO show this to the user somehow
95117
return new ProjectMapping(p, null);
96118
}
97119
return new ProjectMapping(p, any.get());
98120
}).toList();
99121

100-
final FilteredList<ProjectMapping> value = new FilteredList<>(FXCollections.observableArrayList(projectMappings));
122+
final ObservableList<ProjectMapping> observableMappings = FXCollections.observableArrayList(projectMappings);
123+
final FilteredList<ProjectMapping> value = new FilteredList<>(observableMappings);
101124
filterOnlyWorkCheckBox.selectedProperty().addListener(((observable, oldValue, newValue) -> {
102125
if (Boolean.TRUE.equals(newValue))
103126
value.setPredicate(pm -> pm.getProject().isWork());
104127
else
105128
value.setPredicate(null);
106129
}));
107130
filterOnlyWorkCheckBox.setSelected(true);
108-
//value.add(new ProjectMapping(null, null)); // TODO somehow allow to create a new project for a task
131+
109132
mappingTableView.setItems(value);
110133

111134
// KeepTime Project column
@@ -114,12 +137,14 @@ private void initialize() {
114137
keepTimeColumn.setPrefWidth(200);
115138

116139
// External Project column with dropdown
140+
externalProjects.add(0, null); // option to clear selection
141+
final ObservableList<HeimatTask> externalProjectsObservableList = FXCollections.observableArrayList(externalProjects);
117142
TableColumn<ProjectMapping, HeimatTask> externalColumn = new TableColumn<>("Heimat Project");
118143
externalColumn.setCellValueFactory(data -> new SimpleObjectProperty<>(data.getValue().heimatTask));
119144
externalColumn.setCellFactory(col -> new TableCell<>() {
120145
// TODO search in box would be nice
121146
private final ComboBox<HeimatTask> comboBox = new ComboBox<>(
122-
FXCollections.observableArrayList(externalProjects));
147+
externalProjectsObservableList );
123148

124149
@Override
125150
protected void updateItem(HeimatTask item, boolean empty) {
@@ -170,38 +195,102 @@ protected void updateItem(HeimatTask item, boolean empty) {
170195

171196
mappingTableView.getColumns().addAll(keepTimeColumn, externalColumn);
172197

173-
saveButton.setOnAction((ae) -> {
174-
LOG.debug("New mappings to be saved '{}'.", projectMappings);
175-
final List<ProjectMapping> newMappings = projectMappings.stream()
176-
.filter(pm -> pm.getHeimatTask() != null)
177-
.toList();
178-
179-
final List<ExternalProjectMapping> list = newMappings.stream().map(projectMapping -> {
180-
final Optional<ExternalProjectMapping> any = alreadyMappedProjects.stream()
181-
.filter(pm -> pm.getProject().getId()
182-
== projectMapping.project.getId())
183-
.findAny();
184-
final HeimatTask heimatTask = projectMapping.getHeimatTask();
185-
if (any.isPresent()) {
186-
final ExternalProjectMapping projectMapping1 = any.get();
187-
projectMapping1.setExternalProjectName(heimatTask.projectName());
188-
projectMapping1.setExternalTaskId(heimatTask.id());
189-
projectMapping1.setExternalTaskName(heimatTask.name());
190-
projectMapping1.setExternalTaskMetadata(heimatTask.toString()); // TODO to json
191-
return projectMapping1;
198+
addNewProjectComboBox.setCellFactory(param -> new ListCell<>() {
199+
@Override
200+
protected void updateItem(HeimatTask item, boolean empty) {
201+
super.updateItem(item, empty);
202+
if (item == null || empty) {
203+
setGraphic(null);
204+
setText(null);
205+
} else {
206+
// TODO maybe show if the project was already mapped
207+
setText(item.projectName() + " - " + item.name());
208+
}
209+
}
210+
});
211+
addNewProjectComboBox.setButtonCell(new ListCell<>() {
212+
@Override
213+
protected void updateItem(HeimatTask item, boolean empty) {
214+
super.updateItem(item, empty);
215+
if (empty || item == null) {
216+
setText(null);
217+
} else {
218+
setText(item.projectName() + " - " + item.name());
192219
}
193-
return new ExternalProjectMapping(ExternalSystem.Heimat, heimatTask.projectName(), heimatTask.id(),
194-
heimatTask.name(), heimatTask.toString()// TODO to json
195-
, projectMapping.project);
196-
}).toList();
197-
198-
externalProjectsMappingsRepository.saveAll(list);
199-
// TODO remove mappings which were removed also from database
200-
// TODO close
220+
}
221+
});
222+
addNewProjectButton.disableProperty()
223+
.bind(addNewProjectComboBox.getSelectionModel().selectedItemProperty().isNull());
224+
addNewProjectButton.setOnAction(ae -> {
225+
final HeimatTask task = addNewProjectComboBox.getValue();
226+
final int sortIndex = model.getAvailableProjects().size();
227+
final Project project = controller.addNewProject(
228+
new Project(task.projectName() + " - " + task.name(), task.bookingHint(), Color.BLACK, true, sortIndex));
229+
observableMappings.add(new ProjectMapping(project, task));
230+
addNewProjectComboBox.getSelectionModel().clearSelection();
231+
});
232+
addNewProjectComboBox.setItems(FXCollections.observableArrayList(externalProjects));
233+
234+
saveButton.setOnAction((ae) -> {
235+
LOG.debug("New mappings to be saved '{}'.", observableMappings);
236+
237+
final List<ExternalProjectMapping> mappingsToCreateOrUpdate = observableMappings.stream()
238+
.filter(
239+
pm -> pm.getHeimatTask()
240+
!= null)
241+
.map(projectMapping -> {
242+
final Optional<ExternalProjectMapping> any = alreadyMappedProjects.stream()
243+
.filter(
244+
pm -> pm.getProject()
245+
.getId()
246+
== projectMapping.project.getId())
247+
.findAny();
248+
final HeimatTask heimatTask = projectMapping.getHeimatTask();
249+
if (any.isPresent()) {
250+
final ExternalProjectMapping projectMapping1 = any.get();
251+
projectMapping1.setExternalProjectName(
252+
heimatTask.projectName());
253+
projectMapping1.setExternalTaskId(
254+
heimatTask.id());
255+
projectMapping1.setExternalTaskName(
256+
heimatTask.name());
257+
projectMapping1.setExternalTaskMetadata(
258+
heimatTask.toString()); // TODO to json
259+
return projectMapping1;
260+
}
261+
return new ExternalProjectMapping(
262+
ExternalSystem.Heimat,
263+
heimatTask.projectName(),
264+
heimatTask.id(),
265+
heimatTask.name(),
266+
heimatTask.toString()
267+
// TODO to json
268+
,
269+
projectMapping.project);
270+
})
271+
.toList();
272+
// TODO the list also contains unchanged mappings
273+
externalProjectsMappingsRepository.saveAll(mappingsToCreateOrUpdate);
274+
275+
// remove mappings which were removed also from database
276+
final List<ExternalProjectMapping> mappingsToRemove = alreadyMappedProjects.stream()
277+
.filter(
278+
em -> observableMappings.stream()
279+
.anyMatch(
280+
wantedMapping ->
281+
wantedMapping.project.getId()
282+
== em.getProject().getId()
283+
&&
284+
wantedMapping.heimatTask
285+
== null))
286+
.toList();
287+
externalProjectsMappingsRepository.deleteAll(mappingsToRemove);
288+
289+
thisStage.close();
201290
});
202291

203292
cancelButton.setOnAction(ae -> {
204-
// TODO Close
293+
thisStage.close();
205294
});
206295
}
207296

src/main/java/de/doubleslash/keeptime/view/SettingsController.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -447,7 +447,7 @@ private void showMapProjectsStage() throws IOException {
447447
// TODO somehow the url is not available after saving - only when restarted
448448
MapExternalProjectsController settingsController = fxmlLoader2.getController();
449449
Stage settingsStage = new Stage();
450-
//settingsController.setStage(settingsStage);
450+
settingsController.setStage(settingsStage);
451451
settingsStage.initModality(Modality.APPLICATION_MODAL);
452452
settingsStage.setTitle("External Project Mappings");
453453
settingsStage.setResizable(false);

src/main/resources/layouts/externalProjectMapping.fxml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,21 @@
22

33
<?import javafx.scene.control.Button?>
44
<?import javafx.scene.control.CheckBox?>
5+
<?import javafx.scene.control.ComboBox?>
6+
<?import javafx.scene.control.DatePicker?>
57
<?import javafx.scene.control.Label?>
68
<?import javafx.scene.control.TableView?>
79
<?import javafx.scene.layout.AnchorPane?>
810

9-
1011
<AnchorPane prefHeight="600.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="de.doubleslash.keeptime.view.MapExternalProjectsController">
1112
<children>
1213
<TableView fx:id="mappingTableView" layoutY="50.0" prefHeight="500.0" prefWidth="600.0" />
1314
<Label text="Map projects" />
1415
<Button fx:id="saveButton" layoutX="429.0" layoutY="555.0" mnemonicParsing="false" text="Save" />
1516
<Button fx:id="cancelButton" layoutX="500.0" layoutY="555.0" mnemonicParsing="false" text="Cancel" />
16-
<CheckBox fx:id="filterOnlyWorkCheckBox" layoutX="11.0" layoutY="29.0" mnemonicParsing="false" text="Only Work items" />
17-
<CheckBox layoutX="334.0" layoutY="29.0" mnemonicParsing="false" text="Only favorites" />
17+
<CheckBox fx:id="filterOnlyWorkCheckBox" layoutX="14.0" layoutY="21.0" mnemonicParsing="false" text="Only Work items" />
18+
<ComboBox fx:id="addNewProjectComboBox" layoutX="14.0" layoutY="555.0" prefHeight="25.0" prefWidth="210.0" />
19+
<Button fx:id="addNewProjectButton" layoutX="234.0" layoutY="555.0" mnemonicParsing="false" text="+" />
20+
<DatePicker fx:id="tasksForDateDatePicker" layoutX="412.0" layoutY="17.0" />
1821
</children>
1922
</AnchorPane>

0 commit comments

Comments
 (0)