1616
1717package de .doubleslash .keeptime .view ;
1818
19+ import de .doubleslash .keeptime .controller .Controller ;
1920import de .doubleslash .keeptime .model .ExternalProjectMapping ;
2021import de .doubleslash .keeptime .model .ExternalSystem ;
2122import de .doubleslash .keeptime .model .Model ;
2728import javafx .beans .property .SimpleObjectProperty ;
2829import javafx .beans .property .SimpleStringProperty ;
2930import javafx .collections .FXCollections ;
31+ import javafx .collections .ObservableList ;
3032import javafx .collections .transformation .FilteredList ;
3133import javafx .fxml .FXML ;
3234import javafx .scene .control .*;
35+ import javafx .scene .paint .Color ;
36+ import javafx .stage .Stage ;
3337import org .slf4j .Logger ;
3438import org .slf4j .LoggerFactory ;
3539import org .springframework .stereotype .Component ;
3640
41+ import java .time .LocalDate ;
3742import java .util .List ;
3843import 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
0 commit comments