ShoppingList.java
package no.ntnu.idatt1002.demo.view.scenes;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import no.ntnu.idatt1002.demo.Logger;
import no.ntnu.idatt1002.demo.UpdateableScene;
import no.ntnu.idatt1002.demo.dao.DAO;
import no.ntnu.idatt1002.demo.dao.DBConnectionProvider;
import no.ntnu.idatt1002.demo.data.Event;
import no.ntnu.idatt1002.demo.data.InventoryItem;
import no.ntnu.idatt1002.demo.data.QuantityUnit;
import no.ntnu.idatt1002.demo.data.Recipe;
import no.ntnu.idatt1002.demo.data.ShoppingListItem;
import no.ntnu.idatt1002.demo.repo.EventRegister;
import no.ntnu.idatt1002.demo.repo.InventoryRegister;
import no.ntnu.idatt1002.demo.repo.ItemRegister;
import no.ntnu.idatt1002.demo.repo.RecipeRegister;
import no.ntnu.idatt1002.demo.repo.ShoppingListItemRegister;
import no.ntnu.idatt1002.demo.view.components.AddPopup;
import no.ntnu.idatt1002.demo.view.components.Field;
import no.ntnu.idatt1002.demo.view.components.ListHeader;
import no.ntnu.idatt1002.demo.view.components.PrimaryButton;
import no.ntnu.idatt1002.demo.view.components.ShoppingListItemPane;
import no.ntnu.idatt1002.demo.view.components.ShoppingListItemPopup;
/**
* The shopping list page
*/
public class ShoppingList extends VBox implements UpdateableScene {
// Positioning containers
private final HBox contentContainer;
private final VBox innerContentContainer;
// Shopping list containers
private final VBox shoppingListContainer;
private final ScrollPane shoppingListScrollPane;
// Static top and bottom bars
private final HBox staticListTopBar;
private final HBox staticListBottomBar;
// Items list
private Map<Integer, ShoppingListItem> items;
// List of selected items
List<Integer> selectedItemsId = new ArrayList<>();
// List header sort
String sortBy = "Name";
String filterBy = "All";
/**
* Constructor for the ShoppingList class.
*/
public ShoppingList() {
super();
// Initialize the containers
contentContainer = new HBox(); // Page content container
innerContentContainer = new VBox(); // Inner content container
shoppingListContainer = new VBox(); // Shopping list container
shoppingListScrollPane = new ScrollPane(); // Shopping list scroll pane
staticListTopBar = new HBox(); // Static top bar
staticListBottomBar = new HBox(); // Static bottom bar
// Top bar labels
Label topBarItemName = new Label("Name");
Label topBarItemCategory = new Label("Category");
Label topBarItemQuantity = new Label("Quantity");
Label topBarItemUnit = new Label("Unit");
Label topBarItemCheckBox = new Label("Done");
// Bottom bar labels
Label bottomBarTotalQuantity = new Label("Total quantity: 0");
Label bottomBarTotalPrice = new Label("Total price: 0");
// Add the labels to the top bar
staticListTopBar.getChildren().addAll(
topBarItemName,
topBarItemCategory,
topBarItemQuantity,
topBarItemUnit,
topBarItemCheckBox);
// Add the labels to the bottom bar
staticListBottomBar.getChildren().addAll(bottomBarTotalQuantity, bottomBarTotalPrice);
// Scroll pane settings
shoppingListScrollPane.setContent(shoppingListContainer);
shoppingListScrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
shoppingListScrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.AS_NEEDED);
// Clear all button and clear completed button container
HBox clearButtonsContainer = new HBox();
// Clear all button and clear completed button
PrimaryButton clearAllButton = new PrimaryButton("Delete all");
clearAllButton.setOnAction(e -> {
ShoppingListItemRegister shoppingListItemRegister = new ShoppingListItemRegister(
new DAO(new DBConnectionProvider()));
shoppingListItemRegister.getAllItems();
shoppingListItemRegister.clearShoppingList();
items = shoppingListItemRegister.getItems();
loadShoppingList();
});
PrimaryButton clearCompletedButton = new PrimaryButton("Clear completed");
clearCompletedButton.setOnAction(e -> {
checkForSelectedItems();
ShoppingListItemRegister shoppingListItemRegister = new ShoppingListItemRegister(
new DAO(new DBConnectionProvider()));
shoppingListItemRegister.getAllItems();
items = shoppingListItemRegister.getItems();
shoppingListContainer.getChildren().forEach(node -> {
ShoppingListItemPane shoppingListItemPane = (ShoppingListItemPane) node;
if (shoppingListItemPane.isSelected()) {
// Get the item
ShoppingListItem item = shoppingListItemRegister.getShoppingListItemById(
shoppingListItemPane.getShoppingListItemId());
// Add the item to the Inventory
InventoryRegister inventoryRegister = new InventoryRegister(
new DAO(new DBConnectionProvider()));
inventoryRegister.addInventoryItem(
item.getItemId(),
item.getQuantity(),
item.getUnit(),
20240430);
// Remove the item from the shopping list
shoppingListItemRegister.deleteFromShoppingList(item.getId());
selectedItemsId.removeIf(id -> id == item.getId());
}
shoppingListItemRegister.getAllItems();
items = shoppingListItemRegister.getItems();
});
loadShoppingList();
});
clearButtonsContainer.getChildren().addAll(
clearAllButton,
clearCompletedButton);
// Add the containers to the content container
innerContentContainer.getChildren().addAll(
clearButtonsContainer,
staticListTopBar,
shoppingListScrollPane,
staticListBottomBar);
contentContainer.getChildren().add(innerContentContainer);
// Create the list header and add to the page
ListHeader shoppingListHeader = new ListHeader();
shoppingListHeader.setOnSearch(this::fullSearch);
shoppingListHeader.setOnSearchQueryChange(this::search);
shoppingListHeader.setOnAdd(this::addItem);
shoppingListHeader.setOnSortChange(this::sortBy);
super.getChildren().addAll(shoppingListHeader, contentContainer);
// TODO: Create a connection to the database for the add button
// TODO remove test data after testing
// ItemRegister itemRegister = new ItemRegister(new DAO(new
// DBConnectionProvider()));
// itemRegister.getAllItems();
// Map<Integer, Item> inventoryItems = itemRegister.getItems();
// // Add the items to the shopping list TODO fix the unit to match the item
// inventoryItems.values().forEach(item -> addItem(new
// ShoppingListItem(item.getId(), item.getName(), item.getCategory(),
// item.getAllergy(), 1, "kg")));
// Initialize the itemsList
ShoppingListItemRegister shoppingListItemRegister = new ShoppingListItemRegister(
new DAO(new DBConnectionProvider()));
shoppingListItemRegister.getAllItems();
items = shoppingListItemRegister.getItems();
loadShoppingList();
// CSS styling
contentContainer.getStyleClass().add("centered");
shoppingListContainer.getStyleClass().addAll("shopping-list");
innerContentContainer.getStyleClass().add("shopping-list");
staticListTopBar.getStyleClass().addAll("static-label-container", "rounded-top");
staticListBottomBar.getStyleClass().addAll("static-bottom-label-container", "rounded-bottom");
clearButtonsContainer.getStyleClass().add("clear-button-container");
// Labels
topBarItemName.getStyleClass().add("static-label");
topBarItemCategory.getStyleClass().add("static-label");
topBarItemQuantity.getStyleClass().add("static-label");
topBarItemUnit.getStyleClass().add("static-label");
topBarItemCheckBox.getStyleClass().add("static-label");
bottomBarTotalPrice.getStyleClass().add("static-label");
bottomBarTotalQuantity.getStyleClass().add("static-label");
// Buttons
clearAllButton.getStyleClass().addAll("red-button", "clear-button");
clearCompletedButton.getStyleClass().addAll("red-button", "clear-button");
}
/**
* Method to add a new shoppingList item to the database and the shopping list.
* <p>
* Uses the {@link ShoppingListItemRegister#addToShoppingList(int, int, String)
* addToShoppingList} method to add the item to the database.
* </p>
* xs * @param item the item to be added
*/
public void addItem() {
AddPopup addPopup = new AddPopup("ShoppingListItem");
addPopup.show(this.getScene().getWindow());
ItemRegister itemRegister = new ItemRegister(new DAO(new DBConnectionProvider()));
itemRegister.getAllItems();
addPopup.addField(Field.ofMap("Item", itemRegister.getItems()));
addPopup.addField(Field.ofNumber("Quantity"));
addPopup.addField(Field.ofString("Unit"));
addPopup.setOnAdd((Object[] o) -> {
Object[] itemIdAsList = (Object[]) o[0];
itemRegister.getAllItems();
int itemId = (int) itemIdAsList[0];
int quantity = (int) o[1];
String unit = (String) o[2];
try {
ShoppingListItemRegister shoppingListItemRegister = new ShoppingListItemRegister(
new DAO(new DBConnectionProvider()));
shoppingListItemRegister.addToShoppingList(itemId, quantity, unit);
shoppingListItemRegister.getAllItems();
items = shoppingListItemRegister.getItems();
} catch (Exception e) {
Logger.fatal("Failed to add item");
e.printStackTrace();
}
loadShoppingList();
});
}
/**
* Method to update a shopping list item.
* <p>
* Uses the
* {@link ShoppingListItemRegister#updateShoppingListItem(int, int, int, String)
* updateShoppingListItem} method to update the item in the database.
* </p>
*
* @param values the values to update
*/
public void updateShoppingListItem(Object[] values) {
// get the register
ShoppingListItemRegister shoppingListItemRegister = new ShoppingListItemRegister(
new DAO(new DBConnectionProvider()));
// get all items
shoppingListItemRegister.getAllItems();
// get the values
int shoppingListItemId = (int) values[0]; // id of the shopping list item
ShoppingListItem shoppingListItem = shoppingListItemRegister.getShoppingListItemById(
shoppingListItemId); // shopping
// Item id
Object[] itemIdAsList = (Object[]) values[1]; // id of the item as a list
int itemId = -1; // id of the item
if (itemIdAsList == null) {
itemId = shoppingListItem.getItemId();
} else {
itemId = (int) itemIdAsList[0];
}
// Quantity
int quantity = -1; // quantity of the item
if (values[2] == null) {
quantity = shoppingListItem.getQuantity();
} else {
quantity = (int) values[2];
}
// Unit
String unit = null; // unit of the item
if (values[3] == null) {
Logger.info("Unit is null");
unit = shoppingListItem.getUnit();
} else {
unit = (String) values[3];
}
shoppingListItemRegister.updateShoppingListItem(
shoppingListItemId,
itemId,
quantity,
unit);
shoppingListItemRegister.getAllItems();
items = shoppingListItemRegister.getItems();
loadShoppingList();
}
/**
* Method to delete a shopping list item.
*
* @param shoppingListItemId the id of the shopping list item to delete
*/
public void deleteShoppingListItem(int shoppingListItemId) {
ShoppingListItemRegister shoppingListItemRegister = new ShoppingListItemRegister(
new DAO(new DBConnectionProvider()));
shoppingListItemRegister.getAllItems();
shoppingListItemRegister.deleteFromShoppingList(shoppingListItemId);
shoppingListItemRegister.getAllItems();
items = shoppingListItemRegister.getItems();
loadShoppingList();
}
/**
* Method to search for items in the shopping list.
*
* @param query string to search for
*/
public void fullSearch(String query) {
search(query);
this.requestFocus();
}
/**
* Method to search for items in the shopping list updates search on query
* change.
*
* @param query string to search for
*/
public void search(String query) {
ShoppingListItemRegister register = new ShoppingListItemRegister(
new DAO(new DBConnectionProvider()));
register.searchItemsByName(query);
items = register.getItems();
if (query.isEmpty()) {
register.getAllItems();
items = register.getItems();
loadShoppingList();
} else {
loadShoppingList();
}
}
private void sortBy(String sortBy) {
this.sortBy = sortBy;
loadShoppingList();
}
/**
* Method to switch the filter by.
*
* @param filterBy the filter to switch by
*/
private void filterBy(String filterBy) {
this.filterBy = filterBy;
loadShoppingList();
}
/**
* Method to check for selected items.
* <p>
* Clears the selected items list and adds the selected items to the list
* </p>
*/
private void checkForSelectedItems() {
selectedItemsId.clear();
shoppingListContainer.getChildren().forEach(node -> {
ShoppingListItemPane shoppingListItemPane = (ShoppingListItemPane) node;
if (shoppingListItemPane.isSelected()) {
selectedItemsId.add(shoppingListItemPane.getShoppingListItemId());
}
});
}
/**
* Method to update the page.
* <p>
* Clears the displayed list and fills it with new items
* </p>
*/
private void loadShoppingList() {
checkForSelectedItems();
shoppingListContainer.getChildren().clear();
// Sort the items based on the sort by
ShoppingListItemRegister shoppingListItemRegister = new ShoppingListItemRegister(
new DAO(new DBConnectionProvider()));
shoppingListItemRegister.getAllItems();
items = shoppingListItemRegister.getItems();
List<ShoppingListItem> sortedItems = new ArrayList<>(items.values());
switch (sortBy) {
case "Name":
sortedItems.sort(Comparator.comparing(ShoppingListItem::getName));
break;
case "Date":
sortedItems.sort(Comparator.comparing(ShoppingListItem::getQuantity));
break;
case "Type":
sortedItems.sort(Comparator.comparing(ShoppingListItem::getCategory));
break;
default:
break;
}
// Clear the shopping list container
shoppingListContainer.getChildren().clear();
sortedItems.forEach(item -> {
ShoppingListItemPane shoppingListItemPane = new ShoppingListItemPane(item);
shoppingListItemPane.setOnMouseClicked(v -> {
ShoppingListItemPopup shoppingListItemPopup = new ShoppingListItemPopup(item);
shoppingListItemPopup.setOnSave(this::updateShoppingListItem);
shoppingListItemPopup.setOnDelete(this::deleteShoppingListItem);
shoppingListItemPopup.show(this.getScene().getWindow());
});
if (selectedItemsId.contains(item.getId())) {
shoppingListItemPane.setSelected();
}
shoppingListItemPane.getStyleClass().add("shopping-list-item-pane");
shoppingListContainer.getChildren().add(shoppingListItemPane);
});
staticListBottomBar.getChildren().clear();
int itemCount = items.size();
int totalPrice = 0;
Label bottomBarTotalQuantity = new Label("Total quantity: " + itemCount);
Label bottomBarTotalPrice = new Label("Total price: " + totalPrice);
bottomBarTotalQuantity.getStyleClass().add("static-label");
bottomBarTotalPrice.getStyleClass().add("static-label");
staticListBottomBar.getChildren().addAll(bottomBarTotalQuantity, bottomBarTotalPrice);
}
public void updateScene() {
getItemsToAddToList();
loadShoppingList();
}
public VBox createScene() {
return this;
}
private void getItemsToAddToList() {
// Get all the necessary registers
InventoryRegister inventoryRegister = new InventoryRegister(
new DAO(new DBConnectionProvider()));
ShoppingListItemRegister shoppingListItemRegister = new ShoppingListItemRegister(
new DAO(new DBConnectionProvider()));
EventRegister eventRegister = new EventRegister(
new DAO(new DBConnectionProvider()));
RecipeRegister recipeRegister = new RecipeRegister(
new DAO(new DBConnectionProvider()));
// Get the items from the database
inventoryRegister.getAllInventoryItems();
shoppingListItemRegister.getAllItems();
eventRegister.getAllEvents();
recipeRegister.getAllRecipes();
// add the items to lists
List<InventoryItem> inventoryItems = inventoryRegister.getInventoryItems();
Map<Integer, ShoppingListItem> shoppingListItems = shoppingListItemRegister.getItems();
Map<Integer, Event> eventItems = eventRegister.getEvents();
Map<Integer, Recipe> recipeItems = recipeRegister.getRecipes();
// Calculate the amount of each item in the inventory and shopping list
// Create a map of the existing items in the inventory
Map<Integer, QuantityUnit> existingItems = inventoryItems.stream()
.collect(Collectors.toMap(
InventoryItem::getItemId, // Key mapper (Item ID)
item -> {
// Convert quantity to common unit and create QuantityUnit object
return convertQuantity(item.getUnit(), item.getQuantity());
},
// Merging function: add quantities of duplicate keys and keep the unit
(quantityUnit1, quantityUnit2) -> {
quantityUnit1.addQuantity(quantityUnit2.getQuantity());
return quantityUnit1;
}));
// Add the items from the shopping list to the existing items
shoppingListItems.values().forEach(item -> {
if (existingItems.containsKey(item.getItemId())) {
QuantityUnit existingItem = convertQuantity(item.getUnit(), item.getQuantity());
existingItems.get(item.getItemId()).addQuantity(existingItem.getQuantity());
} else {
QuantityUnit newItem = existingItems.put(
item.getItemId(), convertQuantity(item.getUnit(), item.getQuantity()));
}
});
// Create a map of the items needed
Map<Integer, QuantityUnit> neededItems = new HashMap<>();
// Get the items needed from the events
eventItems.forEach((id, event) -> recipeItems.get(event.getRecipeId())
.getIngredients().forEach(item -> neededItems.put(
item.getItemId(),
convertQuantity(item.getUnit(), item.getQuantity()))));
// Create a map of the items to add
Map<Integer, QuantityUnit> itemsToAdd = new HashMap<>();
// Calculate the difference between the existing items and the needed items
neededItems.forEach((id, quantityUnit) -> {
if (existingItems.containsKey(id)) {
QuantityUnit existingItem = existingItems.get(id);
if (existingItem.getQuantity() < quantityUnit.getQuantity()) {
itemsToAdd.put(id, new QuantityUnit(
quantityUnit.getQuantity() - existingItem.getQuantity(),
quantityUnit.getUnit()));
}
} else {
itemsToAdd.put(id, quantityUnit);
}
});
// Add the items to the shopping list
itemsToAdd.forEach((id, quantityUnit) -> {
shoppingListItemRegister.addToShoppingList(id, quantityUnit.getQuantity(), quantityUnit.getUnit());
});
}
private static QuantityUnit convertQuantity(String unit, int quantity) {
return switch (unit.toLowerCase()) {
case "kg" -> new QuantityUnit(quantity * 1000, "g");
case "hg" -> new QuantityUnit(quantity * 100, "g");
case "l" -> new QuantityUnit(quantity * 1000, "ml");
case "dl" -> new QuantityUnit(quantity * 10, "ml");
case "pck" -> new QuantityUnit(quantity * 20, "pcs");
default -> new QuantityUnit(quantity, unit);
};
}
}