Edit shopping category, mods to ingredient table edits

version2
Tim Holloway 2 years ago
parent e6817d7c2e
commit 200317d177
  1. 51
      src/main/java/com/mousetech/gourmetj/EditShopcatBean.java
  2. 215
      src/main/java/com/mousetech/gourmetj/RecipeDetailBean.java
  3. 2
      src/main/java/com/mousetech/gourmetj/persistence/dao/ShopcatRepository.java
  4. 4
      src/main/java/com/mousetech/gourmetj/persistence/model/Ingredient.java
  5. 5
      src/main/java/com/mousetech/gourmetj/persistence/service/RecipeService.java
  6. 18
      src/main/resources/META-INF/resources/WEB-INF/layout/layout.xhtml
  7. 46
      src/main/resources/META-INF/resources/detailEdit.xhtml
  8. 1
      src/main/resources/META-INF/resources/editShopcat.xhtml

@ -5,14 +5,12 @@ import java.util.List;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import javax.faces.event.AjaxBehaviorEvent; import javax.faces.event.AjaxBehaviorEvent;
import javax.faces.event.ValueChangeEvent;
import javax.faces.view.ViewScoped; import javax.faces.view.ViewScoped;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
import org.primefaces.event.SelectEvent;
import com.mousetech.gourmetj.persistence.dao.ShopcatRepository; import com.mousetech.gourmetj.persistence.dao.ShopcatRepository;
import com.mousetech.gourmetj.persistence.model.Shopcat;
@Named @Named
@ViewScoped @ViewScoped
@ -34,6 +32,18 @@ public class EditShopcatBean implements Serializable {
@Inject @Inject
ShopcatRepository shopcatRepository; ShopcatRepository shopcatRepository;
/*
* Indicate if the shopcatname has been changed.
*/
private boolean changed = false;
/**
* @return the changed
*/
public boolean isChanged() {
return changed;
}
/** /**
* Default Constructor. * Default Constructor.
*/ */
@ -45,15 +55,20 @@ public class EditShopcatBean implements Serializable {
public void init() { public void init() {
this.shopcatSuggestionList = this.shopcatRepository this.shopcatSuggestionList = this.shopcatRepository
.findDistinctCategoryNative(); .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 * @return the shopcatName
*/ */
@ -89,6 +104,11 @@ public class EditShopcatBean implements Serializable {
return ingkey; return ingkey;
} }
/**
* Set the ingredient key.
*
* @param ingkey
*/
public void setIngkey(String ingkey) { public void setIngkey(String ingkey) {
this.ingkey = ingkey; this.ingkey = ingkey;
} }
@ -99,8 +119,17 @@ public class EditShopcatBean implements Serializable {
public List<String> getShopcatSuggestionList() { public List<String> getShopcatSuggestionList() {
return shopcatSuggestionList; return shopcatSuggestionList;
} }
public void ajaxShopcatSuggest(AjaxBehaviorEvent event) { public void ajaxShopcatSuggest(AjaxBehaviorEvent event) {
this.shopcatName = this.shopcatSuggestion; this.shopcatName = this.shopcatSuggestion;
} }
/**
* ValueChangeListener for shopcat editor.
*
* @param e Event, with new name in it.
*/
public void shopcatNameChanged(ValueChangeEvent e) {
this.changed = true;
}
} }

@ -10,6 +10,7 @@ import java.util.Set;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import javax.faces.event.AjaxBehaviorEvent; import javax.faces.event.AjaxBehaviorEvent;
import javax.faces.event.ValueChangeEvent;
import javax.faces.model.DataModel; import javax.faces.model.DataModel;
import javax.faces.model.ListDataModel; import javax.faces.model.ListDataModel;
import javax.faces.view.ViewScoped; import javax.faces.view.ViewScoped;
@ -203,6 +204,7 @@ public class RecipeDetailBean implements Serializable {
ingredients ingredients
.setWrappedData(new ArrayList<IngredientUI>(1)); .setWrappedData(new ArrayList<IngredientUI>(1));
} }
log.info("Ingredient size="+ingredients.getRowCount());
return ingredients; return ingredients;
} }
@ -224,7 +226,7 @@ public class RecipeDetailBean implements Serializable {
*/ */
if (this.recipe == null) { if (this.recipe == null) {
Long rid = Long rid =
//(Long) JSFUtils.getFlash("recipeID"); // (Long) JSFUtils.getFlash("recipeID");
userSession.getLastEdit(); userSession.getLastEdit();
if (rid != null) { if (rid != null) {
this.recipe = loadRecipe(rid); this.recipe = loadRecipe(rid);
@ -357,6 +359,7 @@ public class RecipeDetailBean implements Serializable {
public String doAddIngredient() { public String doAddIngredient() {
this.addIngredientList(this.getIngredientText()); this.addIngredientList(this.getIngredientText());
setIngredientText(""); // clear for next entry setIngredientText(""); // clear for next entry
updateSelectionStatus();
return null; return null;
} }
@ -376,7 +379,67 @@ public class RecipeDetailBean implements Serializable {
doAddIngredient(); 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<IngredientUI> 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()) { if (!isMoveUpAble()) {
JSFUtils.addErrorMessage("Cannot move up."); JSFUtils.addErrorMessage("Cannot move up.");
return; return;
@ -393,6 +456,15 @@ public class RecipeDetailBean implements Serializable {
} }
} }
this.setDirty(); this.setDirty();
auditRows(rows);
}
private void auditRows(List<IngredientUI> 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 * @param eventUnused
*/ */
public void ajaxMoveDown(AjaxBehaviorEvent event) { public void ajaxMoveDown() {
if (!isMoveDownAble()) { if (!isMoveDownAble()) {
JSFUtils.addErrorMessage("Cannot move down."); JSFUtils.addErrorMessage("Cannot move down.");
return; return;
@ -416,9 +488,10 @@ public class RecipeDetailBean implements Serializable {
rows.add(i, r); rows.add(i, r);
} }
} }
auditRows(rows);
} }
public void ajaxDeleteItems(AjaxBehaviorEvent event) { public void ajaxDeleteItems() {
final List<IngredientUI> rows = getWrappedIngredients(); final List<IngredientUI> rows = getWrappedIngredients();
List<IngredientUI> selectedRows = List<IngredientUI> selectedRows =
new ArrayList<IngredientUI>(); new ArrayList<IngredientUI>();
@ -433,61 +506,81 @@ public class RecipeDetailBean implements Serializable {
for (IngredientUI row : selectedRows) { for (IngredientUI row : selectedRows) {
rows.remove(row); rows.remove(row);
} }
auditRows(rows);
} }
// ===== // =====
@Inject @Inject
EditShopcatBean editShopcatBean; 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) { public void ajaxEditShopcat(IngredientUI item) {
// Map<String, Object> options = new HashMap<>(); editShopcatBean.beginEdit(item.getIngkey(),
// options.put("resizable", false); item.getShopCat());
// TODO: Reject if ingkey is empty/null
editShopcatBean.setIngkey(item.getIngkey());
editShopcatBean.setShopcatName(item.getShopCat());
// if ( ! StringUtils.isBlank(key) ) {
// PrimeFaces.current().dialog().openDynamic("editShopcat");
// }
} }
/**
* On "OK" for edit shopcat where shopcat has changed,
* update the shopcat Entity and the ingredients.
*/
public void doUpdateShopcat() { public void doUpdateShopcat() {
final String key = editShopcatBean.getIngkey(); final String key = editShopcatBean.getIngkey();
if ( StringUtils.isBlank(key)) { if (StringUtils.isBlank(key)) {
return; // Do not set category if no ingKey return; // Do not set category if no ingKey
} }
final String catname = editShopcatBean.getShopcatName(); final String catname = editShopcatBean.getShopcatName();
Shopcat sc = this.recipeService Shopcat sc = this.recipeService
.findShopcatForIngredientKey(catname); .findShopcatForIngredientKey(key);
if (sc == null) { if (sc == null) {
sc = new Shopcat(); sc = new Shopcat();
sc.setIngkey(key); sc.setIngkey(key);
sc.setShopcategory(catname);
} else { } else {
if (catname.equals(sc.getShopcategory())) { if ( StringUtils.equals(sc.getShopcategory(), catname) ) {
// No change. Do nothing. return; // No change
return; }
}
} }
sc.setShopcategory(catname);
if ( StringUtils.isBlank(catname)) {
this.recipeService.deleteShopcat(sc); /*
} else { * 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); this.recipeService.saveShopcat(sc);
} }
updateDisplayedShopcats(key, sc); updateDisplayedShopcats(key, sc);
} }
/** /**
* When Shopcat name changes, update the Ingredients. * When Shopcat name changes, update the Ingredients. In
* In detailEdit, an AJAX "render" will then update * detailEdit, an AJAX "render" will then update the display.
* the display. In recipeDetails, nothing actually shows. * In recipeDetails, nothing actually shows.
*/ */
private void updateDisplayedShopcats(String key, Shopcat sc) { private void updateDisplayedShopcats(String key,
List<IngredientUI> ingList = this.getWrappedIngredients(); Shopcat sc) {
for (IngredientUI ingUI: ingList ) { List<IngredientUI> ingList =
if ( key.equals(ingUI.getIngkey())) { this.getWrappedIngredients();
for (IngredientUI ingUI : ingList) {
if (key.equals(ingUI.getIngkey())) {
ingUI.setShopCat(sc); ingUI.setShopCat(sc);
} }
} }
@ -521,6 +614,7 @@ public class RecipeDetailBean implements Serializable {
} }
addIngredient(line); addIngredient(line);
} }
updateSelectionStatus();
} }
/** /**
@ -541,43 +635,6 @@ public class RecipeDetailBean implements Serializable {
ingredients.add(new IngredientUI(ing)); ingredients.add(new IngredientUI(ing));
} }
public boolean isSelectionActive() {
List<IngredientUI> 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<IngredientUI> 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 Shopcat scat = recipeService
.findShopcatForIngredientKey(ingKey); .findShopcatForIngredientKey(ingKey);
if (scat == null) { if (scat == null) {
JSFUtils.addErrorMessage( log.debug(
"No Shopping Category is defined for Ingredient Key " "No Shopping Category is defined for Ingredient Key "
+ ingKey); + ingKey);
//return false; // return false;
} }
ing.setShopCat(scat); ing.setShopCat(scat);
return true; return true;
} }
/** /**
* Parse out the comma-separated category text control and * Parse out the comma-separated category text control and
* post the results as children of the recipe * post the results as children of the recipe
@ -865,7 +921,6 @@ public class RecipeDetailBean implements Serializable {
public void ajaxUpdateShopcat(IngredientUI item) { public void ajaxUpdateShopcat(IngredientUI item) {
log.warn("SHOPCAT2 "); log.warn("SHOPCAT2 ");
updateShopcat(item); updateShopcat(item);
;
} }
// *** // ***
@ -1002,9 +1057,9 @@ public class RecipeDetailBean implements Serializable {
long now = new java.util.Date().getTime(); long now = new java.util.Date().getTime();
return String.valueOf(now); return String.valueOf(now);
} }
//*** Add Group // *** Add Group
private String newGroupName; private String newGroupName;
/** /**
@ -1020,16 +1075,18 @@ public class RecipeDetailBean implements Serializable {
public void setNewGroupName(String newGroupName) { public void setNewGroupName(String newGroupName) {
this.newGroupName = newGroupName; this.newGroupName = newGroupName;
} }
/** /**
* Add new group to bottom of model as AJAX operation. * Add new group to bottom of model as AJAX operation.
*
* @return null * @return null
*/ */
public void doAddGroup() { public void doAddGroup() {
IngredientUI iui = new IngredientUI(null); IngredientUI iui = new IngredientUI(null);
iui.setIngGroup(true); iui.setIngGroup(true);
iui.setItem(this.getNewGroupName()); iui.setItem(this.getNewGroupName());
List<IngredientUI> ingUIList = this.getWrappedIngredients(); List<IngredientUI> ingUIList =
this.getWrappedIngredients();
ingUIList.add(iui); ingUIList.add(iui);
this.setNewGroupName(""); // Clear for next time! this.setNewGroupName(""); // Clear for next time!
} }

@ -29,4 +29,6 @@ public interface ShopcatRepository
public List<String> findDistinctCategoryNative(); public List<String> findDistinctCategoryNative();
public Shopcat findShopcatByIngkey(String ingkey); public Shopcat findShopcatByIngkey(String ingkey);
public void deleteByIngkey(String key);
} }

@ -58,7 +58,7 @@ public class Ingredient implements Serializable, IngredientIF {
private String item; private String item;
@Column(name = "optional") @Column(name = "optional")
@NotNull //@NotNull
private Integer optional; private Integer optional;
@Column(name = "position") @Column(name = "position")
@ -81,6 +81,8 @@ public class Ingredient implements Serializable, IngredientIF {
private String unit; private String unit;
public Ingredient() { public Ingredient() {
// Attempt to remedy lack of constraints in DB
this.optional = 0;
} }
@Override @Override

@ -90,6 +90,11 @@ public class RecipeService implements Serializable {
} }
public void deleteShopcatByIngKey(String key) {
shopcatRepository.deleteByIngkey(key);
}
public void saveShopcat(Shopcat sc) { public void saveShopcat(Shopcat sc) {
shopcatRepository.save(sc); shopcatRepository.save(sc);
} }

@ -47,12 +47,26 @@
style="z-index: 25000" style="z-index: 25000"
> >
<p:commandButton id="cmdExpiredOK" <p:commandButton id="cmdExpiredOK"
value="OK" value="OK" action="/main.jsf"
action="/main.jsf"
oncomplete="PF('sessionExpiredConfirmation').hide()" oncomplete="PF('sessionExpiredConfirmation').hide()"
/> />
</p:confirmDialog> </p:confirmDialog>
</h:form> </h:form>
<!-- -->
<h:form id="frmOpErr">
<p:commandButton type="button"
onclick="PF('opError').show()"
/>
<p:confirmDialog
message="Session may have expired."
header="Error"
severity="alert" widgetVar="opError"
>
<p:commandButton value="OK"
oncomplete="PF('cd').hide()"
/>
</p:confirmDialog>
</h:form>
</h:body> </h:body>
</f:view> </f:view>
</ui:composition> </ui:composition>

@ -100,7 +100,6 @@
max="10" max="10"
value="#{recipeDetailBean.recipe.preptime}" value="#{recipeDetailBean.recipe.preptime}"
> >
<f:validator validatorId="com.mousetech.gourmetj.utils.TimeValidator"/>
<f:converter <f:converter
converterId="com.mousetech.gourmetj.utils.TimeConverter" converterId="com.mousetech.gourmetj.utils.TimeConverter"
/> />
@ -110,9 +109,8 @@
/> />
<p:inputText id="rcooktime" <p:inputText id="rcooktime"
max="10" max="10"
value="#{recipeDetailBean.recipe.cooktime}" value="#{recipeDetailBean.recipe.cooktime}"
> >
<f:validator validatorId="com.mousetech.gourmetj.utils.TimeValidator"/>
<f:converter <f:converter
converterId="com.mousetech.gourmetj.utils.TimeConverter" converterId="com.mousetech.gourmetj.utils.TimeConverter"
/> />
@ -178,23 +176,17 @@
<p:commandButton value="Up" <p:commandButton value="Up"
disabled="#{not recipeDetailBean.moveUpAble}" disabled="#{not recipeDetailBean.moveUpAble}"
id="ctlUp" id="ctlUp"
> actionListener="#{recipeDetailBean.ajaxMoveUp}"
<f:ajax update="ingredientTable"
listener="#{recipeDetailBean.ajaxMoveUp}" onerror="PF('opError').show()"
execute="ingredientTable" />
render="ingredientTable"
/>
</p:commandButton>
<p:commandButton value="Down" <p:commandButton value="Down"
disabled="#{not recipeDetailBean.moveDownAble}" disabled="#{not recipeDetailBean.moveDownAble}"
id="ctlDown" id="ctlDown"
> onerror="PF('opError').show()"
<f:ajax actionListener="#{recipeDetailBean.ajaxMoveDown}"
listener="#{recipeDetailBean.ajaxMoveDown}" update="ingredientTable"
execute="ingredientTable" />
render="ingredientTable"
/>
</p:commandButton>
<p:commandButton <p:commandButton
value="Add Group" value="Add Group"
onclick="PF('addGroupDlg').show()" onclick="PF('addGroupDlg').show()"
@ -211,17 +203,14 @@
<p:commandButton <p:commandButton
value="Delete" value="Delete"
id="ctlDelete" id="ctlDelete"
disabled="not #{recipeDetailBean.selectionActive}" disabled="#{not recipeDetailBean.selectable}"
> onerror="PF('opError').show()"
<f:ajax actionListener="#{recipeDetailBean.ajaxDeleteItems}"
listener="#{recipeDetailBean.ajaxDeleteItems}" update="ingredientTable"
immediate="true" />
render="ingredientTable"
/>
</p:commandButton>
</h:panelGroup> </h:panelGroup>
<p:dataTable id="ingredientTable" <p:dataTable id="ingredientTable"
style="width: 100%; height: 420px; margin-top: 8px" style="width: 100%; height: 430px; margin-top: 8px"
value="#{recipeDetailBean.ingredients}" value="#{recipeDetailBean.ingredients}"
scrollable="true" scrollable="true"
scrollHeight="380" var="item" scrollHeight="380" var="item"
@ -235,8 +224,8 @@
value="#{item.selected}" value="#{item.selected}"
> >
<f:ajax <f:ajax
immediate="true" listener="#{recipeDetailBean.ajaxSelectionListener}"
render="pnlIngredients" render=":form1:tabGroupClient:ingButtons"
/> />
</p:selectBooleanCheckbox> </p:selectBooleanCheckbox>
</p:column> </p:column>
@ -320,6 +309,7 @@
update="editShopcatDlg" update="editShopcatDlg"
oncomplete="PF('editShopcatDlg').show()" oncomplete="PF('editShopcatDlg').show()"
title="Edit the shopping category for ing. key" title="Edit the shopping category for ing. key"
disabled="#{empty item.ingkey}"
rendered="#{not item.ingGroup}" rendered="#{not item.ingGroup}"
/> />
</p:column> </p:column>

@ -26,6 +26,7 @@
value="Category Name" /> value="Category Name" />
<p:inputText id="ctlShopcat" <p:inputText id="ctlShopcat"
value="#{editShopcatBean.shopcatName}" value="#{editShopcatBean.shopcatName}"
valueChangeListener="#{editShopcatBean.shopcatNameChanged}"
> >
</p:inputText> </p:inputText>
<h:outputText <h:outputText

Loading…
Cancel
Save