diff --git a/src/main/java/com/mousetech/gourmetj/EditShopcatBean.java b/src/main/java/com/mousetech/gourmetj/EditShopcatBean.java index a403a06..d82e896 100644 --- a/src/main/java/com/mousetech/gourmetj/EditShopcatBean.java +++ b/src/main/java/com/mousetech/gourmetj/EditShopcatBean.java @@ -5,14 +5,12 @@ import java.util.List; import javax.annotation.PostConstruct; import javax.faces.event.AjaxBehaviorEvent; +import javax.faces.event.ValueChangeEvent; 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 @@ -34,6 +32,18 @@ public class EditShopcatBean implements Serializable { @Inject ShopcatRepository shopcatRepository; + /* + * Indicate if the shopcatname has been changed. + */ + private boolean changed = false; + + /** + * @return the changed + */ + public boolean isChanged() { + return changed; + } + /** * Default Constructor. */ @@ -45,15 +55,20 @@ public class EditShopcatBean implements Serializable { 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()); -// } } + /** + * Prepare for edit before the dialog is displayed. + * + * @param ingkey Ingredient key + * @param shopCat Previous shopping category for ingKey + */ + public void beginEdit(String ingkey, String shopCat) { + this.setIngkey(ingkey); + this.setShopcatName(shopCat); + this.changed = false; + } + /** * @return the shopcatName */ @@ -89,6 +104,11 @@ public class EditShopcatBean implements Serializable { return ingkey; } + /** + * Set the ingredient key. + * + * @param ingkey + */ public void setIngkey(String ingkey) { this.ingkey = ingkey; } @@ -99,8 +119,17 @@ public class EditShopcatBean implements Serializable { public List getShopcatSuggestionList() { return shopcatSuggestionList; } - + public void ajaxShopcatSuggest(AjaxBehaviorEvent event) { this.shopcatName = this.shopcatSuggestion; } + + /** + * ValueChangeListener for shopcat editor. + * + * @param e Event, with new name in it. + */ + public void shopcatNameChanged(ValueChangeEvent e) { + this.changed = true; + } } diff --git a/src/main/java/com/mousetech/gourmetj/RecipeDetailBean.java b/src/main/java/com/mousetech/gourmetj/RecipeDetailBean.java index 01baab2..d276eb2 100644 --- a/src/main/java/com/mousetech/gourmetj/RecipeDetailBean.java +++ b/src/main/java/com/mousetech/gourmetj/RecipeDetailBean.java @@ -10,6 +10,7 @@ import java.util.Set; import javax.annotation.PostConstruct; import javax.faces.event.AjaxBehaviorEvent; +import javax.faces.event.ValueChangeEvent; import javax.faces.model.DataModel; import javax.faces.model.ListDataModel; import javax.faces.view.ViewScoped; @@ -203,6 +204,7 @@ public class RecipeDetailBean implements Serializable { ingredients .setWrappedData(new ArrayList(1)); } + log.info("Ingredient size="+ingredients.getRowCount()); return ingredients; } @@ -224,7 +226,7 @@ public class RecipeDetailBean implements Serializable { */ if (this.recipe == null) { Long rid = - //(Long) JSFUtils.getFlash("recipeID"); + // (Long) JSFUtils.getFlash("recipeID"); userSession.getLastEdit(); if (rid != null) { this.recipe = loadRecipe(rid); @@ -357,6 +359,7 @@ public class RecipeDetailBean implements Serializable { public String doAddIngredient() { this.addIngredientList(this.getIngredientText()); setIngredientText(""); // clear for next entry + updateSelectionStatus(); return null; } @@ -376,7 +379,67 @@ public class RecipeDetailBean implements Serializable { doAddIngredient(); } - public void ajaxMoveUp(AjaxBehaviorEvent event) { + // === + /** + * Listen to the SELECT checkboxes on Ingredients and update + * the action button statuses. + * + * @param event notused + */ + public void ajaxSelectionListener(AjaxBehaviorEvent event) { + updateSelectionStatus(); + } + + private void updateSelectionStatus() { + List ingList = getWrappedIngredients(); + final int ingCount = ingList.size(); + + boolean moveUpable = true; + boolean moveDownable = true; + boolean selectable = false; + + for (int i = 0; i < ingCount; i++) { + boolean selected = ingList.get(i).isSelected(); + if ((i == 0) && selected) { + moveUpable = false; + } + if ((i == (ingCount - 1)) && selected) { + moveDownable = false; + } + selectable |= selected; + } + this.setMoveUpAble(moveUpable && selectable); + this.setMoveDownAble(moveDownable && selectable); + this.setSelectable(selectable); + } + + // --- + public void setMoveUpAble(boolean moveUpable) { + this.moveUpable = moveUpable; + } + + public void setMoveDownAble(boolean moveDownable) { + this.moveDownable = moveDownable; + } + + public void setSelectable(boolean selectable) { + this.selectable = selectable; + } + + public boolean isMoveUpAble() { + return this.moveUpable; + } + + public boolean isMoveDownAble() { + return this.moveDownable; + } + + public boolean isSelectable() { + return this.selectable; + } + + // --- + public void ajaxMoveUp() { if (!isMoveUpAble()) { JSFUtils.addErrorMessage("Cannot move up."); return; @@ -393,6 +456,15 @@ public class RecipeDetailBean implements Serializable { } } this.setDirty(); + auditRows(rows); + } + + private void auditRows(List rows) { + log.info("=== AUDIT ROWS ==="); + for ( IngredientUI row : rows ) { + log.info((row.isSelected() ? "[X]" : "[ ]" ) +" ROW="+row); + } + log.info("=== DONE ==="); } /** @@ -400,7 +472,7 @@ public class RecipeDetailBean implements Serializable { * * @param eventUnused */ - public void ajaxMoveDown(AjaxBehaviorEvent event) { + public void ajaxMoveDown() { if (!isMoveDownAble()) { JSFUtils.addErrorMessage("Cannot move down."); return; @@ -416,9 +488,10 @@ public class RecipeDetailBean implements Serializable { rows.add(i, r); } } + auditRows(rows); } - public void ajaxDeleteItems(AjaxBehaviorEvent event) { + public void ajaxDeleteItems() { final List rows = getWrappedIngredients(); List selectedRows = new ArrayList(); @@ -433,61 +506,81 @@ public class RecipeDetailBean implements Serializable { for (IngredientUI row : selectedRows) { rows.remove(row); } + auditRows(rows); } // ===== @Inject EditShopcatBean editShopcatBean; + private boolean moveUpable; + + private boolean moveDownable; + + private boolean selectable; + + /** + * Invoked when the "E"(dit" button for Ingkey shopping + * category has been clicked. + * + * @param item The item whose ingredient key will + * have its shopping category edited. Resets the dialog + * backing bean internal state. + */ 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"); -// } + editShopcatBean.beginEdit(item.getIngkey(), + item.getShopCat()); } - + + /** + * On "OK" for edit shopcat where shopcat has changed, + * update the shopcat Entity and the ingredients. + */ public void doUpdateShopcat() { + final String key = editShopcatBean.getIngkey(); - if ( StringUtils.isBlank(key)) { + if (StringUtils.isBlank(key)) { return; // Do not set category if no ingKey } - + final String catname = editShopcatBean.getShopcatName(); Shopcat sc = this.recipeService - .findShopcatForIngredientKey(catname); + .findShopcatForIngredientKey(key); 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.equals(sc.getShopcategory(), catname) ) { + return; // No change + } } - - if ( StringUtils.isBlank(catname)) { - this.recipeService.deleteShopcat(sc); - } else { + sc.setShopcategory(catname); + + /* + * Because the database does not have a UNIQUE + * constraint on ingkeys, we must delete old + * shopcat(s) for this key before adding (updating) + * the new shopcat. + */ + this.recipeService.deleteShopcatByIngKey(key); + if (! StringUtils.isBlank(catname)) { 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. + * 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())) { + private void updateDisplayedShopcats(String key, + Shopcat sc) { + List ingList = + this.getWrappedIngredients(); + for (IngredientUI ingUI : ingList) { + if (key.equals(ingUI.getIngkey())) { ingUI.setShopCat(sc); } } @@ -521,6 +614,7 @@ public class RecipeDetailBean implements Serializable { } addIngredient(line); } + updateSelectionStatus(); } /** @@ -541,43 +635,6 @@ public class RecipeDetailBean implements Serializable { ingredients.add(new IngredientUI(ing)); } - public boolean isSelectionActive() { - List rows = getWrappedIngredients(); - for (IngredientUI row : rows) { - if (row.isSelected()) { - return true; - } - } - return false; - } - - public void setSelectionActive(boolean value) { - // This is required by JBoss JSF, but the property is - // read-only. - } - - public void setMoveUpAble() { - - } - - public void setMoveDownAble() { - - } - - public boolean isMoveUpAble() { - return true; - // TODO: -// if (isSelectionActive()) { -// List rows = getWrappedIngredients(); -// return !rows.get(0).isSelected(); -// } -// return false; - } - - public boolean isMoveDownAble() { - return true; - } - // === /** @@ -695,16 +752,15 @@ public class RecipeDetailBean implements Serializable { Shopcat scat = recipeService .findShopcatForIngredientKey(ingKey); if (scat == null) { - JSFUtils.addErrorMessage( + log.debug( "No Shopping Category is defined for Ingredient Key " + ingKey); - //return false; - + // return false; } ing.setShopCat(scat); return true; } - + /** * Parse out the comma-separated category text control and * post the results as children of the recipe @@ -865,7 +921,6 @@ public class RecipeDetailBean implements Serializable { public void ajaxUpdateShopcat(IngredientUI item) { log.warn("SHOPCAT2 "); updateShopcat(item); - ; } // *** @@ -1002,9 +1057,9 @@ public class RecipeDetailBean implements Serializable { long now = new java.util.Date().getTime(); return String.valueOf(now); } - - //*** Add Group - + + // *** Add Group + private String newGroupName; /** @@ -1020,16 +1075,18 @@ public class RecipeDetailBean implements Serializable { public void setNewGroupName(String newGroupName) { this.newGroupName = newGroupName; } - + /** * Add new group to bottom of model as AJAX operation. + * * @return null */ public void doAddGroup() { IngredientUI iui = new IngredientUI(null); iui.setIngGroup(true); iui.setItem(this.getNewGroupName()); - List ingUIList = this.getWrappedIngredients(); + List ingUIList = + this.getWrappedIngredients(); ingUIList.add(iui); this.setNewGroupName(""); // Clear for next time! } diff --git a/src/main/java/com/mousetech/gourmetj/persistence/dao/ShopcatRepository.java b/src/main/java/com/mousetech/gourmetj/persistence/dao/ShopcatRepository.java index 40c3c85..164fa39 100644 --- a/src/main/java/com/mousetech/gourmetj/persistence/dao/ShopcatRepository.java +++ b/src/main/java/com/mousetech/gourmetj/persistence/dao/ShopcatRepository.java @@ -29,4 +29,6 @@ public interface ShopcatRepository public List findDistinctCategoryNative(); public Shopcat findShopcatByIngkey(String ingkey); + + public void deleteByIngkey(String key); } diff --git a/src/main/java/com/mousetech/gourmetj/persistence/model/Ingredient.java b/src/main/java/com/mousetech/gourmetj/persistence/model/Ingredient.java index 0c5aecb..c74f989 100644 --- a/src/main/java/com/mousetech/gourmetj/persistence/model/Ingredient.java +++ b/src/main/java/com/mousetech/gourmetj/persistence/model/Ingredient.java @@ -58,7 +58,7 @@ public class Ingredient implements Serializable, IngredientIF { private String item; @Column(name = "optional") - @NotNull + //@NotNull private Integer optional; @Column(name = "position") @@ -81,6 +81,8 @@ public class Ingredient implements Serializable, IngredientIF { private String unit; public Ingredient() { + // Attempt to remedy lack of constraints in DB + this.optional = 0; } @Override 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 7913ccc..b4a1877 100644 --- a/src/main/java/com/mousetech/gourmetj/persistence/service/RecipeService.java +++ b/src/main/java/com/mousetech/gourmetj/persistence/service/RecipeService.java @@ -90,6 +90,11 @@ public class RecipeService implements Serializable { } + public void deleteShopcatByIngKey(String key) { + shopcatRepository.deleteByIngkey(key); + } + + public void saveShopcat(Shopcat sc) { shopcatRepository.save(sc); } 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 d537bf7..2abefbc 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 @@ -47,12 +47,26 @@ style="z-index: 25000" > + + + + + + + diff --git a/src/main/resources/META-INF/resources/detailEdit.xhtml b/src/main/resources/META-INF/resources/detailEdit.xhtml index afed994..5525b04 100644 --- a/src/main/resources/META-INF/resources/detailEdit.xhtml +++ b/src/main/resources/META-INF/resources/detailEdit.xhtml @@ -100,7 +100,6 @@ max="10" value="#{recipeDetailBean.recipe.preptime}" > - @@ -110,9 +109,8 @@ /> - @@ -178,23 +176,17 @@ - - + actionListener="#{recipeDetailBean.ajaxMoveUp}" + update="ingredientTable" + onerror="PF('opError').show()" + /> - - + onerror="PF('opError').show()" + actionListener="#{recipeDetailBean.ajaxMoveDown}" + update="ingredientTable" + /> - - + disabled="#{not recipeDetailBean.selectable}" + onerror="PF('opError').show()" + actionListener="#{recipeDetailBean.ajaxDeleteItems}" + update="ingredientTable" + /> @@ -320,6 +309,7 @@ update="editShopcatDlg" oncomplete="PF('editShopcatDlg').show()" title="Edit the shopping category for ing. key" + disabled="#{empty item.ingkey}" rendered="#{not item.ingGroup}" /> diff --git a/src/main/resources/META-INF/resources/editShopcat.xhtml b/src/main/resources/META-INF/resources/editShopcat.xhtml index 0b370ca..d8e917e 100644 --- a/src/main/resources/META-INF/resources/editShopcat.xhtml +++ b/src/main/resources/META-INF/resources/editShopcat.xhtml @@ -26,6 +26,7 @@ value="Category Name" />