Merge branch 'AddOverview' into 'main'
last points of 4.5 Closes #83 and #81 See merge request cse1105/2025-2026/teams/csep-team-76!92
This commit is contained in:
commit
3e8442e517
6 changed files with 419 additions and 22 deletions
|
|
@ -24,6 +24,8 @@ import java.util.function.Consumer;
|
||||||
import javafx.beans.binding.Bindings;
|
import javafx.beans.binding.Bindings;
|
||||||
import javafx.event.ActionEvent;
|
import javafx.event.ActionEvent;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.Node;
|
||||||
|
import javafx.scene.Scene;
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.Button;
|
||||||
import javafx.scene.control.ComboBox;
|
import javafx.scene.control.ComboBox;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
|
|
@ -37,6 +39,8 @@ import javafx.scene.layout.HBox;
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
import javafx.scene.text.Font;
|
import javafx.scene.text.Font;
|
||||||
import javafx.stage.DirectoryChooser;
|
import javafx.stage.DirectoryChooser;
|
||||||
|
import javafx.stage.Modality;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Controller for the recipe detail view.
|
* Controller for the recipe detail view.
|
||||||
|
|
@ -132,7 +136,6 @@ public class RecipeDetailCtrl implements LocaleAware {
|
||||||
*
|
*
|
||||||
* @throws IOException Upon invalid recipe response.
|
* @throws IOException Upon invalid recipe response.
|
||||||
* @throws InterruptedException Upon request interruption.
|
* @throws InterruptedException Upon request interruption.
|
||||||
*
|
|
||||||
* @see FoodpalApplicationCtrl#refresh()
|
* @see FoodpalApplicationCtrl#refresh()
|
||||||
*/
|
*/
|
||||||
private void refresh() throws IOException, InterruptedException {
|
private void refresh() throws IOException, InterruptedException {
|
||||||
|
|
@ -171,9 +174,9 @@ public class RecipeDetailCtrl implements LocaleAware {
|
||||||
this.recipeView = new ScalableRecipeView(recipe, scale);
|
this.recipeView = new ScalableRecipeView(recipe, scale);
|
||||||
// TODO i18n
|
// TODO i18n
|
||||||
inferredKcalLabel.textProperty().bind(Bindings.createStringBinding(() ->
|
inferredKcalLabel.textProperty().bind(Bindings.createStringBinding(() ->
|
||||||
String.format("Inferred %.1f kcal/100g for this recipe",
|
String.format("Inferred %.1f kcal/100g for this recipe",
|
||||||
Double.isNaN(this.recipeView.scaledKcalProperty().get()) ?
|
Double.isNaN(this.recipeView.scaledKcalProperty().get()) ?
|
||||||
0.0 : this.recipeView.scaledKcalProperty().get())
|
0.0 : this.recipeView.scaledKcalProperty().get())
|
||||||
, this.recipeView.scaledKcalProperty()));
|
, this.recipeView.scaledKcalProperty()));
|
||||||
recipeView.servingsProperty().set(servingsSpinner.getValue());
|
recipeView.servingsProperty().set(servingsSpinner.getValue());
|
||||||
inferredServeSizeLabel.textProperty().bind(Bindings.createStringBinding(
|
inferredServeSizeLabel.textProperty().bind(Bindings.createStringBinding(
|
||||||
|
|
@ -197,7 +200,7 @@ public class RecipeDetailCtrl implements LocaleAware {
|
||||||
* @param recipeConsumer The helper function to use when updating the recipe -
|
* @param recipeConsumer The helper function to use when updating the recipe -
|
||||||
* how to update it
|
* how to update it
|
||||||
* @return The created callback to use in a setUpdateCallback or related
|
* @return The created callback to use in a setUpdateCallback or related
|
||||||
* function
|
* function
|
||||||
*/
|
*/
|
||||||
private <T> Consumer<T> createUpdateRecipeCallback(BiConsumer<Recipe, T> recipeConsumer) {
|
private <T> Consumer<T> createUpdateRecipeCallback(BiConsumer<Recipe, T> recipeConsumer) {
|
||||||
return recipes -> {
|
return recipes -> {
|
||||||
|
|
@ -337,6 +340,7 @@ public class RecipeDetailCtrl implements LocaleAware {
|
||||||
PrintExportService.exportToFile(recipeText, dirPath, filename);
|
PrintExportService.exportToFile(recipeText, dirPath, filename);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Toggles the favourite status of the currently viewed recipe in the
|
* Toggles the favourite status of the currently viewed recipe in the
|
||||||
* application configuration and writes the changes to disk.
|
* application configuration and writes the changes to disk.
|
||||||
|
|
@ -425,12 +429,36 @@ public class RecipeDetailCtrl implements LocaleAware {
|
||||||
langSelector.getItems().addAll(Config.languages);
|
langSelector.getItems().addAll(Config.languages);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
public void handleAddAllToShoppingList(ActionEvent actionEvent) {
|
public void handleAddAllToShoppingList(ActionEvent actionEvent) {
|
||||||
System.out.println("handleAddAllToShoppingList");
|
Recipe ingredientSource = (recipeView != null) ? recipeView.getScaled() : recipe;
|
||||||
// TODO BACKLOG Add overview screen
|
|
||||||
recipe.getIngredients().stream()
|
var ingredients = ingredientSource.getIngredients().stream()
|
||||||
.filter(x -> x.getClass().equals(FormalIngredient.class))
|
.map(ri -> {
|
||||||
.map(FormalIngredient.class::cast)
|
if (ri instanceof FormalIngredient fi) {
|
||||||
.forEach(x -> shoppingListService.putIngredient(x, recipe));
|
return fi;
|
||||||
|
}
|
||||||
|
return new FormalIngredient(
|
||||||
|
ri.getIngredient(),
|
||||||
|
1,
|
||||||
|
""
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
var pair = client.UI.getFXML().load(
|
||||||
|
client.scenes.shopping.AddOverviewCtrl.class,
|
||||||
|
"client", "scenes", "shopping", "AddOverview.fxml"
|
||||||
|
);
|
||||||
|
|
||||||
|
var ctrl = pair.getKey();
|
||||||
|
ctrl.setContext(recipe, ingredients);
|
||||||
|
|
||||||
|
Stage stage = new Stage();
|
||||||
|
stage.setTitle("Add to shopping list");
|
||||||
|
stage.initModality(Modality.WINDOW_MODAL);
|
||||||
|
stage.initOwner(((Node) actionEvent.getSource()).getScene().getWindow());
|
||||||
|
stage.setScene(new Scene(pair.getValue()));
|
||||||
|
stage.showAndWait();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
244
client/src/main/java/client/scenes/shopping/AddOverviewCtrl.java
Normal file
244
client/src/main/java/client/scenes/shopping/AddOverviewCtrl.java
Normal file
|
|
@ -0,0 +1,244 @@
|
||||||
|
package client.scenes.shopping;
|
||||||
|
|
||||||
|
import client.service.ShoppingListService;
|
||||||
|
import client.utils.LocaleAware;
|
||||||
|
import client.utils.LocaleManager;
|
||||||
|
import com.google.inject.Inject;
|
||||||
|
import commons.FormalIngredient;
|
||||||
|
import commons.Ingredient;
|
||||||
|
import commons.Recipe;
|
||||||
|
import javafx.beans.property.DoubleProperty;
|
||||||
|
import javafx.beans.property.SimpleDoubleProperty;
|
||||||
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
|
import javafx.beans.property.StringProperty;
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.control.TableColumn;
|
||||||
|
import javafx.scene.control.TableView;
|
||||||
|
import javafx.scene.control.TextInputDialog;
|
||||||
|
import javafx.scene.control.cell.ComboBoxTableCell;
|
||||||
|
import javafx.scene.control.cell.TextFieldTableCell;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
import javafx.util.converter.DoubleStringConverter;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public class AddOverviewCtrl implements LocaleAware {
|
||||||
|
|
||||||
|
private final ShoppingListService shoppingListService;
|
||||||
|
private final LocaleManager localeManager;
|
||||||
|
|
||||||
|
|
||||||
|
private String sourceRecipeName;
|
||||||
|
|
||||||
|
private final ObservableList<AddOverviewRow> rows = FXCollections.observableArrayList();
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private TableView<AddOverviewRow> overviewTable;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private TableColumn<AddOverviewRow, String> nameColumn;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private TableColumn<AddOverviewRow, Double> amountColumn;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private TableColumn<AddOverviewRow, String> unitColumn;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void initialize() {
|
||||||
|
initializeComponents();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final List<String> DEFAULT_UNITS =
|
||||||
|
List.of("", "g", "kg", "ml", "l", "tbsp");
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public AddOverviewCtrl(ShoppingListService shoppingListService, LocaleManager localeManager) {
|
||||||
|
this.shoppingListService = shoppingListService;
|
||||||
|
this.localeManager = localeManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initializeComponents() {
|
||||||
|
overviewTable.setEditable(true);
|
||||||
|
|
||||||
|
nameColumn.setCellValueFactory(c -> c.getValue().nameProperty());
|
||||||
|
amountColumn.setCellValueFactory(c -> c.getValue().amountProperty().asObject());
|
||||||
|
unitColumn.setCellValueFactory(c -> c.getValue().unitProperty());
|
||||||
|
|
||||||
|
nameColumn.setCellFactory(TextFieldTableCell.forTableColumn());
|
||||||
|
nameColumn.setOnEditCommit(e -> e.getRowValue().setName(e.getNewValue()));
|
||||||
|
|
||||||
|
amountColumn.setCellFactory(TextFieldTableCell.forTableColumn(new DoubleStringConverter()));
|
||||||
|
amountColumn.setOnEditCommit(e -> e.getRowValue().setAmount(
|
||||||
|
e.getNewValue() == null ? 0.0 : e.getNewValue()
|
||||||
|
));
|
||||||
|
|
||||||
|
var unitOptions = FXCollections.observableArrayList(DEFAULT_UNITS);
|
||||||
|
unitColumn.setCellFactory(ComboBoxTableCell.forTableColumn(unitOptions));
|
||||||
|
unitColumn.setOnEditCommit(e -> e.getRowValue().setUnit(e.getNewValue()));
|
||||||
|
|
||||||
|
overviewTable.setItems(rows);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateText() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LocaleManager getLocaleManager() {
|
||||||
|
return localeManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setContext(Recipe recipe, List<FormalIngredient> ingredients) {
|
||||||
|
this.sourceRecipeName = recipe == null ? null : recipe.getName();
|
||||||
|
|
||||||
|
rows.clear();
|
||||||
|
for (FormalIngredient fi : ingredients) {
|
||||||
|
rows.add(AddOverviewRow.fromFormalIngredient(fi));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private void handleAddRow() {
|
||||||
|
TextInputDialog nameDialog = new TextInputDialog();
|
||||||
|
nameDialog.setTitle("Add item");
|
||||||
|
nameDialog.setHeaderText("Add an ingredient");
|
||||||
|
nameDialog.setContentText("Name:");
|
||||||
|
|
||||||
|
Optional<String> nameOpt = nameDialog.showAndWait();
|
||||||
|
if (nameOpt.isEmpty() || nameOpt.get().isBlank()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TextInputDialog amountDialog = new TextInputDialog("0");
|
||||||
|
amountDialog.setTitle("Add item");
|
||||||
|
amountDialog.setHeaderText("Amount");
|
||||||
|
amountDialog.setContentText("Amount (number):");
|
||||||
|
|
||||||
|
double amount = 0.0;
|
||||||
|
Optional<String> amountOpt = amountDialog.showAndWait();
|
||||||
|
if (amountOpt.isPresent()) {
|
||||||
|
try {
|
||||||
|
amount = Double.parseDouble(amountOpt.get().trim());
|
||||||
|
} catch (NumberFormatException ignored) {
|
||||||
|
amount = 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rows.add(AddOverviewRow.arbitrary(nameOpt.get().trim(), amount, ""));
|
||||||
|
overviewTable.getSelectionModel().selectLast();
|
||||||
|
overviewTable.scrollTo(rows.size() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private void handleRemoveSelected() {
|
||||||
|
AddOverviewRow selected = overviewTable.getSelectionModel().getSelectedItem();
|
||||||
|
if (selected == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rows.remove(selected);
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private void handleConfirm() {
|
||||||
|
for (AddOverviewRow row : rows) {
|
||||||
|
FormalIngredient fi = row.toFormalIngredient();
|
||||||
|
if (sourceRecipeName == null || sourceRecipeName.isBlank()) {
|
||||||
|
shoppingListService.putIngredient(fi);
|
||||||
|
} else {
|
||||||
|
shoppingListService.putIngredient(fi, sourceRecipeName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closeWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private void handleCancel() {
|
||||||
|
closeWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void closeWindow() {
|
||||||
|
Stage stage = (Stage) overviewTable.getScene().getWindow();
|
||||||
|
stage.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class AddOverviewRow {
|
||||||
|
private Ingredient backingIngredient;
|
||||||
|
|
||||||
|
private final StringProperty name = new SimpleStringProperty("");
|
||||||
|
private final DoubleProperty amount = new SimpleDoubleProperty(0.0);
|
||||||
|
private final StringProperty unit = new SimpleStringProperty("");
|
||||||
|
|
||||||
|
public static AddOverviewRow fromFormalIngredient(FormalIngredient fi) {
|
||||||
|
AddOverviewRow r = new AddOverviewRow();
|
||||||
|
r.backingIngredient = fi.getIngredient();
|
||||||
|
r.name.set(fi.getIngredient().getName());
|
||||||
|
r.amount.set(fi.getAmount());
|
||||||
|
r.unit.set(fi.getUnitSuffix() == null ? "" : fi.getUnitSuffix());
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AddOverviewRow arbitrary(String name, double amount, String unit) {
|
||||||
|
AddOverviewRow r = new AddOverviewRow();
|
||||||
|
r.backingIngredient = null;
|
||||||
|
r.name.set(name == null ? "" : name);
|
||||||
|
r.amount.set(amount);
|
||||||
|
r.unit.set(unit == null ? "" : unit);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FormalIngredient toFormalIngredient() {
|
||||||
|
Ingredient ing = backingIngredient;
|
||||||
|
|
||||||
|
if (ing == null) {
|
||||||
|
ing = new Ingredient(getName(), 0.0, 0.0, 0.0);
|
||||||
|
} else {
|
||||||
|
ing.setName(getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
return new FormalIngredient(ing, getAmount(), getUnit());
|
||||||
|
}
|
||||||
|
|
||||||
|
public StringProperty nameProperty() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DoubleProperty amountProperty() {
|
||||||
|
return amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StringProperty unitProperty() {
|
||||||
|
return unit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name.set(name == null ? "" : name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getAmount() {
|
||||||
|
return amount.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAmount(double amount) {
|
||||||
|
this.amount.set(amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUnit() {
|
||||||
|
return unit.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUnit(String unit) {
|
||||||
|
this.unit.set(unit == null ? "" : unit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,6 +4,7 @@ import client.UI;
|
||||||
import client.service.ShoppingListService;
|
import client.service.ShoppingListService;
|
||||||
import client.utils.LocaleAware;
|
import client.utils.LocaleAware;
|
||||||
import client.utils.LocaleManager;
|
import client.utils.LocaleManager;
|
||||||
|
import client.utils.PrintExportService;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import commons.FormalIngredient;
|
import commons.FormalIngredient;
|
||||||
import javafx.event.ActionEvent;
|
import javafx.event.ActionEvent;
|
||||||
|
|
@ -11,11 +12,14 @@ import javafx.fxml.FXML;
|
||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
import javafx.scene.Parent;
|
import javafx.scene.Parent;
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
|
import javafx.scene.control.Alert;
|
||||||
import javafx.scene.control.ListView;
|
import javafx.scene.control.ListView;
|
||||||
|
import javafx.stage.FileChooser;
|
||||||
import javafx.stage.Modality;
|
import javafx.stage.Modality;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
import javafx.util.Pair;
|
import javafx.util.Pair;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public class ShoppingListCtrl implements LocaleAware {
|
public class ShoppingListCtrl implements LocaleAware {
|
||||||
|
|
@ -47,10 +51,9 @@ public class ShoppingListCtrl implements LocaleAware {
|
||||||
public void initializeComponents() {
|
public void initializeComponents() {
|
||||||
this.shoppingListView.setEditable(true);
|
this.shoppingListView.setEditable(true);
|
||||||
this.shoppingListView.setCellFactory(l -> new ShoppingListCell());
|
this.shoppingListView.setCellFactory(l -> new ShoppingListCell());
|
||||||
this.shoppingListView.getItems().setAll(
|
this.shoppingListView.setItems(this.shopping.getViewModel().getListItems());
|
||||||
this.shopping.getItems()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void refreshList() {
|
private void refreshList() {
|
||||||
this.shoppingListView.getItems().setAll(
|
this.shoppingListView.getItems().setAll(
|
||||||
this.shopping.getItems()
|
this.shopping.getItems()
|
||||||
|
|
@ -63,14 +66,14 @@ public class ShoppingListCtrl implements LocaleAware {
|
||||||
"client", "scenes", "shopping", "ShoppingListItemAddModal.fxml");
|
"client", "scenes", "shopping", "ShoppingListItemAddModal.fxml");
|
||||||
root.getKey().setNewValueConsumer(fi -> {
|
root.getKey().setNewValueConsumer(fi -> {
|
||||||
this.shopping.putIngredient(fi);
|
this.shopping.putIngredient(fi);
|
||||||
refreshList();
|
|
||||||
});
|
});
|
||||||
stage.setScene(new Scene(root.getValue()));
|
stage.setScene(new Scene(root.getValue()));
|
||||||
stage.setTitle("My modal window");
|
stage.setTitle("My modal window");
|
||||||
stage.initModality(Modality.WINDOW_MODAL);
|
stage.initModality(Modality.WINDOW_MODAL);
|
||||||
stage.initOwner(
|
stage.initOwner(
|
||||||
((Node)actionEvent.getSource()).getScene().getWindow() );
|
((Node)actionEvent.getSource()).getScene().getWindow() );
|
||||||
stage.show();
|
stage.showAndWait();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void handleRemoveItem(ActionEvent actionEvent) {
|
public void handleRemoveItem(ActionEvent actionEvent) {
|
||||||
|
|
@ -78,4 +81,39 @@ public class ShoppingListCtrl implements LocaleAware {
|
||||||
this.shopping.getItems().remove(x);
|
this.shopping.getItems().remove(x);
|
||||||
refreshList();
|
refreshList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void handleReset(ActionEvent actionEvent) {
|
||||||
|
shopping.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handlePrint(ActionEvent actionEvent) {
|
||||||
|
FileChooser fileChooser = new FileChooser();
|
||||||
|
fileChooser.setTitle("Save Shopping List");
|
||||||
|
fileChooser.getExtensionFilters().add(
|
||||||
|
new FileChooser.ExtensionFilter("Text Files", "*.txt")
|
||||||
|
);
|
||||||
|
fileChooser.setInitialFileName("shopping-list.txt");
|
||||||
|
|
||||||
|
File file = fileChooser.showSaveDialog(
|
||||||
|
((Node) actionEvent.getSource()).getScene().getWindow()
|
||||||
|
);
|
||||||
|
|
||||||
|
if (file == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
PrintExportService.exportToFile(
|
||||||
|
shopping.makePrintable(),
|
||||||
|
file.getParentFile().toPath(),
|
||||||
|
file.getName()
|
||||||
|
);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Alert alert = new Alert(Alert.AlertType.ERROR);
|
||||||
|
alert.setTitle("Error");
|
||||||
|
alert.setHeaderText("Failed to save shopping list");
|
||||||
|
alert.setContentText(e.getMessage());
|
||||||
|
alert.showAndWait();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,20 +37,45 @@ public class ShoppingListServiceImpl extends ShoppingListService {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void putArbitraryItem(String name) {
|
public void putArbitraryItem(String name) {
|
||||||
|
if (name == null || name.isBlank()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var ingredient = new commons.Ingredient(name.trim(), 0.0, 0.0, 0.0);
|
||||||
|
var fi = new commons.FormalIngredient(ingredient, 0.0, "");
|
||||||
|
getViewModel().getListItems().add(new Pair<>(fi, Optional.empty()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FormalIngredient purgeIngredient(Long id) {
|
public FormalIngredient purgeIngredient(Long id) {
|
||||||
|
if (id == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var item : getViewModel().getListItems()) {
|
||||||
|
FormalIngredient fi = item.getKey();
|
||||||
|
if (fi != null && fi.getId() != null && fi.getId().equals(id)) {
|
||||||
|
getViewModel().getListItems().remove(item);
|
||||||
|
return fi;
|
||||||
|
}
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FormalIngredient purgeIngredient(String ingredientName) {
|
public FormalIngredient purgeIngredient(String ingredientName) {
|
||||||
FormalIngredient fi = getViewModel().getListItems().stream()
|
if (ingredientName == null) {
|
||||||
.filter(i ->
|
return null;
|
||||||
i.getKey().getIngredient().getName().equals(ingredientName))
|
}
|
||||||
.findFirst().orElseThrow(NullPointerException::new).getKey();
|
|
||||||
|
for (var item : getViewModel().getListItems()) {
|
||||||
|
FormalIngredient fi = item.getKey();
|
||||||
|
if (fi != null
|
||||||
|
&& fi.getIngredient() != null
|
||||||
|
&& ingredientName.equals(fi.getIngredient().getName())) {
|
||||||
|
getViewModel().getListItems().remove(item);
|
||||||
|
return fi;
|
||||||
|
}
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -66,6 +91,34 @@ public class ShoppingListServiceImpl extends ShoppingListService {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String makePrintable() {
|
public String makePrintable() {
|
||||||
return "TODO";
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
|
for (var item : getViewModel().getListItems()) {
|
||||||
|
FormalIngredient ingredient = item.getKey();
|
||||||
|
Optional<String> source = item.getValue();
|
||||||
|
|
||||||
|
if (ingredient == null || ingredient.getIngredient() == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.append(ingredient.getIngredient().getName());
|
||||||
|
|
||||||
|
if (ingredient.getAmount() > 0) {
|
||||||
|
sb.append(" - ")
|
||||||
|
.append(ingredient.getAmount());
|
||||||
|
|
||||||
|
if (ingredient.getUnitSuffix() != null && !ingredient.getUnitSuffix().isBlank()) {
|
||||||
|
sb.append(ingredient.getUnitSuffix());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
source.ifPresent(recipe ->
|
||||||
|
sb.append(" (").append(recipe).append(")")
|
||||||
|
);
|
||||||
|
|
||||||
|
sb.append(System.lineSeparator());
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.scene.control.*?>
|
||||||
|
<?import javafx.scene.layout.*?>
|
||||||
|
|
||||||
|
<AnchorPane xmlns="http://javafx.com/javafx/25"
|
||||||
|
xmlns:fx="http://javafx.com/fxml/1"
|
||||||
|
fx:controller="client.scenes.shopping.AddOverviewCtrl"
|
||||||
|
prefHeight="420.0" prefWidth="720.0">
|
||||||
|
|
||||||
|
<VBox spacing="10.0" AnchorPane.topAnchor="10.0" AnchorPane.leftAnchor="10.0"
|
||||||
|
AnchorPane.rightAnchor="10.0" AnchorPane.bottomAnchor="10.0">
|
||||||
|
|
||||||
|
<Label text="Review ingredients before adding" />
|
||||||
|
|
||||||
|
<TableView fx:id="overviewTable" editable="true" VBox.vgrow="ALWAYS">
|
||||||
|
<columns>
|
||||||
|
<TableColumn fx:id="nameColumn" text="Ingredient" prefWidth="360.0"/>
|
||||||
|
<TableColumn fx:id="amountColumn" text="Amount" prefWidth="140.0"/>
|
||||||
|
<TableColumn fx:id="unitColumn" text="Unit" prefWidth="140.0"/>
|
||||||
|
</columns>
|
||||||
|
</TableView>
|
||||||
|
|
||||||
|
<HBox spacing="10.0">
|
||||||
|
<Button text="Add item" onAction="#handleAddRow"/>
|
||||||
|
<Button text="Remove selected" onAction="#handleRemoveSelected"/>
|
||||||
|
<Pane HBox.hgrow="ALWAYS"/>
|
||||||
|
<Button text="Cancel" onAction="#handleCancel"/>
|
||||||
|
<Button text="Confirm & Add" onAction="#handleConfirm"/>
|
||||||
|
</HBox>
|
||||||
|
</VBox>
|
||||||
|
</AnchorPane>
|
||||||
|
|
@ -18,6 +18,8 @@
|
||||||
<HBox>
|
<HBox>
|
||||||
<Button onAction="#handleAddItem">Add</Button>
|
<Button onAction="#handleAddItem">Add</Button>
|
||||||
<Button onAction="#handleRemoveItem">Delete</Button>
|
<Button onAction="#handleRemoveItem">Delete</Button>
|
||||||
|
<Button onAction="#handlePrint">Print</Button>
|
||||||
|
<Button onAction="#handleReset">Reset</Button>
|
||||||
</HBox>
|
</HBox>
|
||||||
</VBox>
|
</VBox>
|
||||||
</TitledPane>
|
</TitledPane>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue