From f03c12cc0f22bf4a5eec33cb17470e2901c4be37 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Thu, 22 Jan 2026 19:52:53 +0100 Subject: [PATCH] feat(client/shopping): create add ingredient modal --- client/src/main/java/client/MyModule.java | 2 + .../ShoppingListNewItemPromptCtrl.java | 96 +++++++++++++++++++ .../shopping/ShoppingListItemAddModal.fxml | 14 +++ 3 files changed, 112 insertions(+) create mode 100644 client/src/main/java/client/scenes/shopping/ShoppingListNewItemPromptCtrl.java create mode 100644 client/src/main/resources/client/scenes/shopping/ShoppingListItemAddModal.fxml diff --git a/client/src/main/java/client/MyModule.java b/client/src/main/java/client/MyModule.java index cf55986..14eaa3e 100644 --- a/client/src/main/java/client/MyModule.java +++ b/client/src/main/java/client/MyModule.java @@ -22,6 +22,7 @@ import client.scenes.nutrition.NutritionViewCtrl; import client.scenes.recipe.IngredientListCtrl; import client.scenes.recipe.RecipeStepListCtrl; import client.scenes.shopping.ShoppingListCtrl; +import client.scenes.shopping.ShoppingListNewItemPromptCtrl; import client.service.ShoppingListService; import client.service.ShoppingListServiceImpl; import client.service.ShoppingListViewModel; @@ -61,6 +62,7 @@ public class MyModule implements Module { binder.bind(new TypeLiteral>() {}).toInstance( new WebSocketDataService<>() ); + binder.bind(ShoppingListNewItemPromptCtrl.class).in(Scopes.SINGLETON); binder.bind(ShoppingListCtrl.class).in(Scopes.SINGLETON); binder.bind(ShoppingListViewModel.class).toInstance(new ShoppingListViewModel()); binder.bind(ShoppingListService.class).to(ShoppingListServiceImpl.class); diff --git a/client/src/main/java/client/scenes/shopping/ShoppingListNewItemPromptCtrl.java b/client/src/main/java/client/scenes/shopping/ShoppingListNewItemPromptCtrl.java new file mode 100644 index 0000000..09bd44f --- /dev/null +++ b/client/src/main/java/client/scenes/shopping/ShoppingListNewItemPromptCtrl.java @@ -0,0 +1,96 @@ +package client.scenes.shopping; + +import client.utils.LocaleAware; +import client.utils.LocaleManager; +import client.utils.server.ServerUtils; +import com.google.inject.Inject; +import commons.FormalIngredient; +import commons.Ingredient; +import commons.Unit; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.event.ActionEvent; +import javafx.scene.control.MenuButton; +import javafx.scene.control.MenuItem; +import javafx.scene.control.Spinner; +import javafx.scene.control.SpinnerValueFactory; +import javafx.stage.Stage; + +import java.io.IOException; +import java.util.Arrays; +import java.util.function.Consumer; +import java.util.function.Function; + +public class ShoppingListNewItemPromptCtrl implements LocaleAware { + public MenuButton ingredientSelection; + private final ObjectProperty selected = new SimpleObjectProperty<>(); + private final ObjectProperty selectedUnit = new SimpleObjectProperty<>(); + private final ServerUtils server; + private final LocaleManager localeManager; + private Consumer newValueConsumer; + public MenuButton unitSelect; + public Spinner amountSelect; + + @Inject + public ShoppingListNewItemPromptCtrl(ServerUtils server, LocaleManager localeManager) { + this.server = server; + this.localeManager = localeManager; + } + + public void setNewValueConsumer(Consumer consumer) { + this.newValueConsumer = consumer; + } + + public void confirmAdd(ActionEvent actionEvent) { + if (selected.get() == null || selectedUnit.get() == null) { + System.err.println("You must select both an ingredient and an unit"); + return; + } + FormalIngredient fi = new FormalIngredient(selected.get(), amountSelect.getValue(), selectedUnit.get().suffix); + newValueConsumer.accept(fi); + Stage stage = (Stage) ingredientSelection.getScene().getWindow(); + stage.close(); + } + + public void cancelAdd(ActionEvent actionEvent) { + } + private void makeMenuItems( + MenuButton menu, + Iterable items, + Function labelMapper, + Consumer onSelect) { + // Iterates over the list of items and applies the label and onSelect handlers. + for (T item : items) { + MenuItem mi = new MenuItem(); + mi.setText(labelMapper.apply(item)); + mi.setOnAction(_ -> { + menu.setText(labelMapper.apply(item)); + onSelect.accept(item); + }); + menu.getItems().add(mi); + } + } + @Override + public void updateText() { + + } + @Override + public void initializeComponents() { + try { + amountSelect.setValueFactory( + new SpinnerValueFactory.DoubleSpinnerValueFactory(0, Double.MAX_VALUE, 0)); + amountSelect.setEditable(true); + makeMenuItems(ingredientSelection, server.getIngredients(), Ingredient::getName, selected::set); + makeMenuItems(unitSelect, + Arrays.stream(Unit.values()).filter(u -> u.formal).toList(), + Unit::toString, selectedUnit::set); + } catch (IOException | InterruptedException e) { + System.err.println(e.getMessage()); + } + } + + @Override + public LocaleManager getLocaleManager() { + return localeManager; + } +} diff --git a/client/src/main/resources/client/scenes/shopping/ShoppingListItemAddModal.fxml b/client/src/main/resources/client/scenes/shopping/ShoppingListItemAddModal.fxml new file mode 100644 index 0000000..31fbc18 --- /dev/null +++ b/client/src/main/resources/client/scenes/shopping/ShoppingListItemAddModal.fxml @@ -0,0 +1,14 @@ + + + + + + + + + + +