From ce0f354f9a9340e8a564ce07bb403ec302f50d3e Mon Sep 17 00:00:00 2001 From: Tim Holloway Date: Wed, 5 Jan 2022 11:02:42 -0500 Subject: [PATCH] Editable shopping category --- pom.xml | 5 + .../mousetech/gourmetj/EditShopcatBean.java | 106 ++++ .../com/mousetech/gourmetj/IngredientUI.java | 4 +- .../java/com/mousetech/gourmetj/JSFUtils.java | 8 + .../mousetech/gourmetj/RecipeDetailBean.java | 95 ++- .../gourmetj/SpringPrimeFacesApplication.java | 37 +- .../persistence/service/RecipeService.java | 9 + .../resources/WEB-INF/layout/layout.xhtml | 54 +- .../META-INF/resources/detailEdit.xhtml | 573 +++++++++--------- .../META-INF/resources/editShopcat.xhtml | 61 ++ .../META-INF/resources/error/error.html | 9 + 11 files changed, 640 insertions(+), 321 deletions(-) create mode 100644 src/main/java/com/mousetech/gourmetj/EditShopcatBean.java create mode 100644 src/main/resources/META-INF/resources/editShopcat.xhtml create mode 100644 src/main/resources/META-INF/resources/error/error.html diff --git a/pom.xml b/pom.xml index cbfbfb3..893b8a5 100644 --- a/pom.xml +++ b/pom.xml @@ -88,6 +88,11 @@ 2.3.3 provided + + javax.servlet + jstl + + org.apache.tomcat.embed tomcat-embed-jasper diff --git a/src/main/java/com/mousetech/gourmetj/EditShopcatBean.java b/src/main/java/com/mousetech/gourmetj/EditShopcatBean.java new file mode 100644 index 0000000..a403a06 --- /dev/null +++ b/src/main/java/com/mousetech/gourmetj/EditShopcatBean.java @@ -0,0 +1,106 @@ +package com.mousetech.gourmetj; + +import java.io.Serializable; +import java.util.List; + +import javax.annotation.PostConstruct; +import javax.faces.event.AjaxBehaviorEvent; +import javax.faces.view.ViewScoped; +import javax.inject.Inject; +import javax.inject.Named; + +import org.primefaces.event.SelectEvent; + +import com.mousetech.gourmetj.persistence.dao.ShopcatRepository; +import com.mousetech.gourmetj.persistence.model.Shopcat; + +@Named +@ViewScoped +public class EditShopcatBean implements Serializable { + + /** + * + */ + private static final long serialVersionUID = 1L; + + private String ingkey; + + private String shopcatName; + + private String shopcatSuggestion; + + private List shopcatSuggestionList; + + @Inject + ShopcatRepository shopcatRepository; + + /** + * Default Constructor. + */ + public EditShopcatBean() { + + } + + @PostConstruct + public void init() { + this.shopcatSuggestionList = this.shopcatRepository + .findDistinctCategoryNative(); +// // Get ingkey from Flash scope +// ingkey = (String) JSFUtils.getFlash("ingkey"); +// Shopcat scat = this.shopcatRepository +// .findShopcatByIngkey(ingkey); +// if (scat != null) { +// this.setShopcatName(scat.getShopcategory()); +// } + } + + /** + * @return the shopcatName + */ + public String getShopcatName() { + return shopcatName; + } + + /** + * @param shopcatName the shopcatName to set + */ + public void setShopcatName(String shopcatName) { + this.shopcatName = shopcatName; + } + + /** + * @return the shopcatSuggestion + */ + public String getShopcatSuggestion() { + return shopcatSuggestion; + } + + /** + * @param shopcatSuggestion the shopcatSuggestion to set + */ + public void setShopcatSuggestion(String shopcatSuggestion) { + this.shopcatSuggestion = shopcatSuggestion; + } + + /** + * @return the ingkey + */ + public String getIngkey() { + return ingkey; + } + + public void setIngkey(String ingkey) { + this.ingkey = ingkey; + } + + /** + * @return the shopcatSuggestionList + */ + public List getShopcatSuggestionList() { + return shopcatSuggestionList; + } + + public void ajaxShopcatSuggest(AjaxBehaviorEvent event) { + this.shopcatName = this.shopcatSuggestion; + } +} diff --git a/src/main/java/com/mousetech/gourmetj/IngredientUI.java b/src/main/java/com/mousetech/gourmetj/IngredientUI.java index 99bd3bf..62f7118 100644 --- a/src/main/java/com/mousetech/gourmetj/IngredientUI.java +++ b/src/main/java/com/mousetech/gourmetj/IngredientUI.java @@ -100,7 +100,6 @@ public class IngredientUI implements IngredientIF { * @see #getAmount */ public String getDisplayAmount() { - // TODO Double amt = ingredient.getAmount(); if (amt == null) { return ""; @@ -120,8 +119,7 @@ public class IngredientUI implements IngredientIF { if (amount.isBlank()) { ingredient.setAmount(null); } - IngredientDigester digester = new IngredientDigester(); - Double[] amt = digester.digestAmount(amount); + Double[] amt = IngredientDigester.digestAmount(amount); ingredient.setAmount(amt[0]); ingredient.setRangeamount(amt[1]); } diff --git a/src/main/java/com/mousetech/gourmetj/JSFUtils.java b/src/main/java/com/mousetech/gourmetj/JSFUtils.java index eaad0fd..f1b9b8c 100644 --- a/src/main/java/com/mousetech/gourmetj/JSFUtils.java +++ b/src/main/java/com/mousetech/gourmetj/JSFUtils.java @@ -83,4 +83,12 @@ public class JSFUtils { return (FacesContext.getCurrentInstance() .getExternalContext().getFlash()); } + + public static Object getFlash(String key) { + return flashScope().get(key); + } + + public static void putFlash(String key, Object value) { + flashScope().put(key, value); + } } diff --git a/src/main/java/com/mousetech/gourmetj/RecipeDetailBean.java b/src/main/java/com/mousetech/gourmetj/RecipeDetailBean.java index 6404634..e31b71d 100644 --- a/src/main/java/com/mousetech/gourmetj/RecipeDetailBean.java +++ b/src/main/java/com/mousetech/gourmetj/RecipeDetailBean.java @@ -2,8 +2,11 @@ package com.mousetech.gourmetj; import java.io.Serializable; import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Set; @@ -17,6 +20,8 @@ import javax.inject.Inject; import javax.inject.Named; import javax.servlet.http.Part; +import org.apache.commons.lang3.StringUtils; +import org.primefaces.PrimeFaces; import org.primefaces.event.FileUploadEvent; import org.primefaces.model.UploadedFile; import org.slf4j.Logger; @@ -30,7 +35,6 @@ import com.mousetech.gourmetj.persistence.model.Recipe; import com.mousetech.gourmetj.persistence.model.Shopcat; import com.mousetech.gourmetj.persistence.service.RecipeService; import com.mousetech.gourmetj.springweb.PictureController; -import com.mousetech.gourmetj.IngredientDigester; /** * Backing bean for display/edit recipe detail @@ -218,8 +222,7 @@ public class RecipeDetailBean implements Serializable { private void init() { this.recipe = userSession.getRecipe(); - - log.info("Using recipe: " + this.recipe ); + log.info("Using recipe: " + this.recipe); /** * For "create new, this recipe is a blank constructed * and passed from main page. For Detail display, it's @@ -243,7 +246,7 @@ public class RecipeDetailBean implements Serializable { getIngredients().setWrappedData( buildIngredientFacade(recipe.getIngredientHash())); - log.info("Set recipe: " + this.recipe ); + log.info("Set recipe: " + this.recipe); } /** @@ -257,7 +260,7 @@ public class RecipeDetailBean implements Serializable { if (recipe == null) { return null; } - + Set cList = recipe.getCategories(); StringBuffer sb = new StringBuffer(35); boolean first = true; @@ -438,6 +441,64 @@ public class RecipeDetailBean implements Serializable { } } + // ===== + @Inject + EditShopcatBean editShopcatBean; + + public void ajaxEditShopcat(IngredientUI item) { +// Map options = new HashMap<>(); +// options.put("resizable", false); + // TODO: Reject if ingkey is empty/null + editShopcatBean.setIngkey(item.getIngkey()); + editShopcatBean.setShopcatName(item.getShopCat()); +// if ( ! StringUtils.isBlank(key) ) { +// PrimeFaces.current().dialog().openDynamic("editShopcat"); +// } + } + + public void doUpdateShopcat() { + final String key = editShopcatBean.getIngkey(); + if ( StringUtils.isBlank(key)) { + return; // Do not set category if no ingKey + } + + final String catname = editShopcatBean.getShopcatName(); + + Shopcat sc = this.recipeService + .findShopcatForIngredientKey(catname); + if (sc == null) { + sc = new Shopcat(); + sc.setIngkey(key); + sc.setShopcategory(catname); + } else { + if (catname.equals(sc.getShopcategory())) { + // No change. Do nothing. + return; + } + } + + if ( StringUtils.isBlank(catname)) { + this.recipeService.deleteShopcat(sc); + } else { + this.recipeService.saveShopcat(sc); + } + updateDisplayedShopcats(key, sc); + } + + /** + * When Shopcat name changes, update the Ingredients. + * In detailEdit, an AJAX "render" will then update + * the display. In recipeDetails, nothing actually shows. + */ + private void updateDisplayedShopcats(String key, Shopcat sc) { + List ingList = this.getWrappedIngredients(); + for (IngredientUI ingUI: ingList ) { + if ( key.equals(ingUI.getIngkey())) { + ingUI.setShopCat(sc); + } + } + } + /** * Bulk add for ingredients. Looks for long input and if * found, tries to split it up and add as multiple @@ -581,7 +642,7 @@ public class RecipeDetailBean implements Serializable { * it. */ private boolean saveIngredients() { - if ( ! updateIngredientList()) { + if (!updateIngredientList()) { return false; } updateRecipeCategories(recipe, category); @@ -596,8 +657,8 @@ public class RecipeDetailBean implements Serializable { iList.clear(); for (IngredientUI iui : saveIng) { Ingredient ing = iui.getIngredient(); - ing.setRecipe(recipe); - if ( ! updateShopcat(iui) ) { + ing.setRecipe(recipe); + if (!updateShopcat(iui)) { return false; } iList.add(ing); @@ -623,7 +684,6 @@ public class RecipeDetailBean implements Serializable { } } - /** * Update shopcat for Ingredient. * @@ -633,17 +693,19 @@ public class RecipeDetailBean implements Serializable { private boolean updateShopcat(IngredientUI ingUI) { final Ingredient ing = ingUI.getIngredient(); final String ingKey = ing.getIngkey(); - if ( (ingKey == null) || (ingKey.isBlank())) { + if ((ingKey == null) || (ingKey.isBlank())) { ing.setIngkey(null); ing.setShopCat(null); return true; } - Shopcat scat = recipeService.findShopcatForIngredientKey(ingKey); - if ( scat == null ) { + Shopcat scat = recipeService + .findShopcatForIngredientKey(ingKey); + if (scat == null) { JSFUtils.addErrorMessage( - "No Shopping Category is defined for Ingredient Key "+ ingKey); + "No Shopping Category is defined for Ingredient Key " + + ingKey); return false; - + } ing.setShopCat(scat); return true; @@ -808,9 +870,10 @@ public class RecipeDetailBean implements Serializable { public void ajaxUpdateShopcat(IngredientUI item) { log.warn("SHOPCAT2 "); - updateShopcat(item);; + updateShopcat(item); + ; } - + // *** public String editDescription() { diff --git a/src/main/java/com/mousetech/gourmetj/SpringPrimeFacesApplication.java b/src/main/java/com/mousetech/gourmetj/SpringPrimeFacesApplication.java index 484fb21..f445f93 100644 --- a/src/main/java/com/mousetech/gourmetj/SpringPrimeFacesApplication.java +++ b/src/main/java/com/mousetech/gourmetj/SpringPrimeFacesApplication.java @@ -1,25 +1,30 @@ package com.mousetech.gourmetj; -import javax.inject.Qualifier; import javax.servlet.ServletContext; import javax.servlet.ServletException; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.domain.EntityScan; -import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.boot.web.server.ErrorPage; +import org.springframework.boot.web.server.ErrorPageRegistrar; +import org.springframework.boot.web.server.ErrorPageRegistry; import org.springframework.boot.web.servlet.ServletContextInitializer; import org.springframework.context.annotation.Bean; -import org.springframework.web.filter.HiddenHttpMethodFilter; +import org.springframework.http.HttpStatus; @SpringBootApplication -@EntityScan(value = {"com.mousetech.gourmetj.persistence.model"}) +@EntityScan(value = { + "com.mousetech.gourmetj.persistence.model" }) public class SpringPrimeFacesApplication { - public static void main(String[] args) { - SpringApplication.run(SpringPrimeFacesApplication.class, args); - } - + final String errorPage = "/error/error.html"; + + public static void main(String[] args) { + SpringApplication.run(SpringPrimeFacesApplication.class, + args); + } + @Bean public ServletContextInitializer initializer() { return new ServletContextInitializer() { @@ -39,4 +44,20 @@ public class SpringPrimeFacesApplication { } }; } + + @Bean + public ErrorPageRegistrar errorPageRegistrar() { + return new ErrorPageRegistrar() { + @Override + public void registerErrorPages( + ErrorPageRegistry registry) { + registry.addErrorPages(new ErrorPage( + HttpStatus.NOT_FOUND, errorPage)); + registry.addErrorPages(new ErrorPage( + HttpStatus.INTERNAL_SERVER_ERROR, + errorPage)); + } + + }; + } } diff --git a/src/main/java/com/mousetech/gourmetj/persistence/service/RecipeService.java b/src/main/java/com/mousetech/gourmetj/persistence/service/RecipeService.java index c2f7529..483b536 100644 --- a/src/main/java/com/mousetech/gourmetj/persistence/service/RecipeService.java +++ b/src/main/java/com/mousetech/gourmetj/persistence/service/RecipeService.java @@ -88,6 +88,15 @@ public class RecipeService implements Serializable { public Shopcat findShopcatForIngredientKey(String ingkey) { return shopcatRepository.findShopcatByIngkey(ingkey); } + + + public void saveShopcat(Shopcat sc) { + shopcatRepository.save(sc); + } + + public void deleteShopcat(Shopcat sc) { + shopcatRepository.delete(sc); + } /** * CategoryService as a sub-function of RecipeService diff --git a/src/main/resources/META-INF/resources/WEB-INF/layout/layout.xhtml b/src/main/resources/META-INF/resources/WEB-INF/layout/layout.xhtml index b664203..81e8ad0 100644 --- a/src/main/resources/META-INF/resources/WEB-INF/layout/layout.xhtml +++ b/src/main/resources/META-INF/resources/WEB-INF/layout/layout.xhtml @@ -1,30 +1,36 @@ - + - - - <ui:insert name="title">Gourmet Recipe Manager (web version)</ui:insert> - - - - - -

- Gourmet Recipe Manager (web version) -

- - - + + + + + + <ui:insert name="title">Gourmet Recipe Manager (web version)</ui:insert> + + + + +

+ Gourmet Recipe Manager (web version) +

+ + + (C) 2021 Tim Holloway, Licensed under the Apache License, Version 2.0. + href="http://www.apache.org/licenses/LICENSE-2.0" + >Apache License, Version 2.0.

Based on Gourmet Recipe Manager by T. Hinkle

-
-
-
\ No newline at end of file +
+
+
+ + \ No newline at end of file diff --git a/src/main/resources/META-INF/resources/detailEdit.xhtml b/src/main/resources/META-INF/resources/detailEdit.xhtml index 87f8b96..71852b4 100644 --- a/src/main/resources/META-INF/resources/detailEdit.xhtml +++ b/src/main/resources/META-INF/resources/detailEdit.xhtml @@ -1,14 +1,18 @@ - + - Gourmet Recipe Manager - - - - - - - - - Description - - - - - - - - - - - + + + + + + Description + - - - - - - - - - - - - - + + + - - - - - - - - - - Ingredients - - - - - - + - - + - - + + + + + + + + + + - - - - + + + + Ingredients + + + + + + + + + + + + - - - - - + + + + + + + + + + Amt. - - - - - + + + + + Units - - - - - + + + + + Item - + + + - - - - + Opt. - - - - + + + + Ing. Key - - - - - - + + + + + + Shop. Cat. - + + + + + + - - - - - - - - - - - - - -
- + + + + + + + + +
+ +
+
+
+ + + -
-
-
- - - - - - - - - - -
-
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/META-INF/resources/editShopcat.xhtml b/src/main/resources/META-INF/resources/editShopcat.xhtml new file mode 100644 index 0000000..0b370ca --- /dev/null +++ b/src/main/resources/META-INF/resources/editShopcat.xhtml @@ -0,0 +1,61 @@ + + + + + Shopping Category + + + + + + + +
Ingredient key: + #{editShopcatBean.ingkey} +
+ + + + + + + + + + + + +
+
+
+
+ \ No newline at end of file diff --git a/src/main/resources/META-INF/resources/error/error.html b/src/main/resources/META-INF/resources/error/error.html new file mode 100644 index 0000000..14f0ea6 --- /dev/null +++ b/src/main/resources/META-INF/resources/error/error.html @@ -0,0 +1,9 @@ + + +ERROR + + +

Uh-oh!

+ An error happened. + + \ No newline at end of file