Editable shopping category

This commit is contained in:
Tim Holloway 2022-01-05 11:02:42 -05:00
parent e4da9a6a71
commit ce0f354f9a
11 changed files with 659 additions and 340 deletions

View File

@ -88,6 +88,11 @@
<version>2.3.3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>

View File

@ -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<String> 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<String> getShopcatSuggestionList() {
return shopcatSuggestionList;
}
public void ajaxShopcatSuggest(AjaxBehaviorEvent event) {
this.shopcatName = this.shopcatSuggestion;
}
}

View File

@ -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]);
}

View File

@ -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);
}
}

View File

@ -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<Category> 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<String, Object> 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<IngredientUI> 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() {

View File

@ -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));
}
};
}
}

View File

@ -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

View File

@ -1,30 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui:composition xmlns:f="http://xmlns.jcp.org/jsf/core"
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:tc="http://myfaces.apache.org/tobago/component"
xmlns:p="http://primefaces.org/ui"
>
<f:view>
<h:head>
<title><ui:insert name="title">Gourmet Recipe Manager (web version)</ui:insert></title>
<link rel="icon" type="image/vnd.microsoft.icon"
href="#{pageContext.contextPath}/favicon.ico"
/>
<h:outputStylesheet name="css/style.css" />
</h:head>
<h:body>
<h1>
<ui:insert name="title">Gourmet Recipe Manager (web version)</ui:insert>
</h1>
<ui:insert name="content">
<ui:include src="content.xhtml" />
</ui:insert>
<head></head>
<body>
<ui:composition>
<f:view>
<h:head>
<title><ui:insert name="title">Gourmet Recipe Manager (web version)</ui:insert></title>
<link rel="icon" type="image/vnd.microsoft.icon"
href="#{pageContext.contextPath}/favicon.ico"
/>
<h:outputStylesheet name="css/style.css" />
</h:head>
<h:body>
<h1>
<ui:insert name="title">Gourmet Recipe Manager (web version)</ui:insert>
</h1>
<ui:insert name="content">
<ui:include src="content.xhtml" />
</ui:insert>
(C) 2021 Tim Holloway, Licensed under the <a
href="http://www.apache.org/licenses/LICENSE-2.0"
>Apache License, Version 2.0</a>.
href="http://www.apache.org/licenses/LICENSE-2.0"
>Apache License, Version 2.0</a>.
<p>Based on Gourmet Recipe Manager by T. Hinkle</p>
</h:body>
</f:view>
</ui:composition>
</h:body>
</f:view>
</ui:composition>
</body>
</html>

View File

@ -1,14 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui:composition template="/WEB-INF/layout/layout.xhtml"
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:p="http://primefaces.org/ui"
xmlns:c="http://xmlns.jcp.org/jstl"
>
<ui:define name="title">Gourmet Recipe Manager</ui:define>
<ui:define name="content">
<style>
<head></head>
<body>
<ui:composition template="/WEB-INF/layout/layout.xhtml">
<ui:define name="title">Gourmet Recipe Manager</ui:define>
<ui:define name="content">
<style>
.ingSel {
width: 3em;
text-align: center;
@ -24,304 +28,333 @@
text-align: left;
}
</style>
<h:messages id="messages" />
<p:panel id="editorPanel"
header="#{recipeDetailBean.recipe.title}"
>
<h:form id="form1" enctype="multipart/form-data">
<p:tabView id="tabGroupClient" orientation="left"
activeIndex="#{userSession.detailTab}"
>
<p:tab id="overviewTab" title="Description">
<p:panelGrid columns="2">
<f:facet name="header">Description</f:facet>
<p:outputLabel for="@next"
value="Title"
/>
<p:inputText id="rtitle" size="45"
required="true" focus="true"
placeholder="A recipe title is required."
value="#{recipeDetailBean.recipe.title}"
>
<f:ajax execute="rtitle"
render="editorPanel"
/>
</p:inputText>
<p:outputLabel for="@next"
value="Category"
/>
<p:inputText id="rcategory"
label="Category"
value="#{recipeDetailBean.category}"
tip="One or more categories, separated by commas (ex: Entree, Soup)"
/>
<p:commandButton
value="&lt;- Suggest"
>
<f:ajax execute="rcategory bxCat"
listener="#{recipeDetailBean.ajaxSuggestCategory}"
render="rcategory"
/>
</p:commandButton>
<p:selectOneMenu id="bxCat"
value="#{recipeDetailBean.catToAdd}"
tip="Recipe category suggestions, based on previous selections"
>
<f:selectItems
value="#{recipeDetailBean.suggestCategory}"
/>
</p:selectOneMenu>
<p:outputLabel for="@next"
value="Cuisine"
/>
<p:autoComplete id="rcuisine"
value="#{recipeDetailBean.recipe.cuisine}"
completeMethod="#{recipeDetailBean.cuisineSuggestions}"
/>
<p:outputLabel for="@next"
value="Rating"
/>
<p:rating id="rrating" max="10"
value="#{recipeDetailBean.recipe.rating}"
/>
<p:outputLabel for="@next"
value="Source"
/>
<p:inputText id="rsource" size="45"
value="#{recipeDetailBean.recipe.source}"
/>
<p:outputLabel for="@next"
value="URL"
/>
<p:inputText id="rurl" size="45"
value="#{recipeDetailBean.recipe.link}"
/>
<p:outputLabel for="@next"
value="Description"
/>
<p:inputTextarea id="description"
rows="10" cols="45"
value="#{recipeDetailBean.recipe.description}"
/>
<p:panel id="picPanel">
<img id="bigPix"
src="/img/picture/?dt=#{recipeDetailBean.currentTime}"
/>
</p:panel>
<p:fileUpload id="ctlUpload"
label="Upload Image"
fileUploadListener="#{recipeDetailBean.ajaxUploadImage}"
global="true" mode="advanced"
multiple="false"
update=":messages picPanel"
auto="true" sizeLimit="1000000"
allowTypes="/(\.|\/)(gif|jpe?g|png)$/"
/>
<p:commandButton id="ctlDelImg"
value="Delete Image"
>
<f:ajax
listener="#{recipeDetailBean.ajaxDeleteImage}"
render="picPanel"
immediate="true"
/>
</p:commandButton>
</p:panelGrid>
</p:tab>
<p:tab id="ingredientsTab"
title="Ingredients"
<h:messages id="messages" />
<p:panel id="editorPanel"
header="#{recipeDetailBean.recipe.title}"
>
<h:form id="form1" enctype="multipart/form-data">
<p:tabView id="tabGroupClient"
orientation="left"
activeIndex="#{userSession.detailTab}"
>
<p:panel id="pnlIngredients">
<f:facet name="header">Ingredients</f:facet>
<h:panelGroup id="ingButtons">
<p:commandButton value="Up"
disabled="#{not recipeDetailBean.moveUpAble}"
id="ctlUp"
<p:tab id="overviewTab"
title="Description"
>
<p:panelGrid columns="2">
<f:facet name="header">Description</f:facet>
<p:outputLabel for="@next"
value="Title"
/>
<p:inputText id="rtitle"
size="45" required="true"
focus="true"
placeholder="A recipe title is required."
value="#{recipeDetailBean.recipe.title}"
>
<f:ajax execute="rtitle"
render="editorPanel"
/>
</p:inputText>
<p:outputLabel for="@next"
value="Category"
/>
<p:inputText id="rcategory"
label="Category"
value="#{recipeDetailBean.category}"
tip="One or more categories, separated by commas (ex: Entree, Soup)"
/>
<p:commandButton
value="&lt;- Suggest"
>
<f:ajax
listener="#{recipeDetailBean.ajaxMoveUp}"
execute="ingredientTable"
render="ingredientTable"
execute="rcategory bxCat"
listener="#{recipeDetailBean.ajaxSuggestCategory}"
render="rcategory"
/>
</p:commandButton>
<p:commandButton value="Down"
disabled="#{not recipeDetailBean.moveDownAble}"
id="ctlDown"
<p:selectOneMenu id="bxCat"
value="#{recipeDetailBean.catToAdd}"
tip="Recipe category suggestions, based on previous selections"
>
<f:ajax
listener="#{recipeDetailBean.ajaxMoveDown}"
execute="ingredientTable"
render="ingredientTable"
<f:selectItems
value="#{recipeDetailBean.suggestCategory}"
/>
</p:commandButton>
<p:button value="Add Group"
disabled="true"
</p:selectOneMenu>
<p:outputLabel for="@next"
value="Cuisine"
/>
<p:button
value="Add Recipe As Ingredient"
disabled="true"
/>
<p:button
value="Import Ingredients"
disabled="true"
/>
<p:button value="Paste" />
<p:commandButton value="Delete"
id="ctlDelete"
disabled="not #{recipeDetailBean.selectionActive}"
>
<f:ajax
listener="#{recipeDetailBean.ajaxDeleteItems}"
immediate="true"
render="ingredientTable"
/>
</p:commandButton>
</h:panelGroup>
<p:dataTable id="ingredientTable"
style="width: 100%; height: 420px; margin-top: 8px"
value="#{recipeDetailBean.ingredients}"
scrollable="true"
scrollHeight="380" var="item"
>
<p:column label="Sel."
align="center"
style="width: 2em"
>
<p:selectBooleanCheckbox
id="selected"
value="#{item.selected}"
>
<f:ajax immediate="true"
render="pnlIngredients"
/>
</p:selectBooleanCheckbox>
</p:column>
<p:column style="width: 3.6em">
<f:facet name="header">
Amt.
</f:facet>
<p:inputText id="ingAmt"
size="3"
value="#{item.displayAmount}"
style="text-align: right"
rendered="#{not item.ingGroup}"
>
</p:inputText>
</p:column>
<p:column style="width: 7em">
<f:facet name="header">
Units
</f:facet>
<p:inputText id="ingUnit"
value="#{item.unit}"
size="10"
rendered="#{not item.ingGroup}"
>
</p:inputText>
</p:column>
<p:column style="width: 26em">
<f:facet name="header">
Item
</f:facet>
<p:inputText id="ingItem"
size="42"
value="#{item.item}"
>
</p:inputText>
</p:column>
<p:column label="Optional"
align="center"
styleClass="ingSel"
>
<f:facet name="header">
Opt.
</f:facet>
<p:selectBooleanCheckbox
id="ingOpt"
value="#{item.optionalCB}"
rendered="#{not item.ingGroup}"
/>
</p:column>
<p:column style="width: 13em">
<f:facet name="header">
Ing. Key
</f:facet>
<p:inputText id="ingKey"
value="#{item.ingkey}"
size="20"
rendered="#{not item.ingGroup}"
>
<f:ajax event="change"
listener="#{recipeDetailBean.ajaxUpdateShopcat(item)}"
render="shopCat"
/>
</p:inputText>
</p:column>
<p:column>
<f:facet name="header">
Shop. Cat.
</f:facet>
<h:outputText id="shopCat"
value="#{item.shopCat}"
rendered="#{not item.ingGroup}"
/>
</p:column>
</p:dataTable>
<h:panelGroup id="pnlIng">
<p:remoteCommand name="ingButton"
action="#{recipeDetailBean.doAddIngredient}"
process="@this ctlAddIngTxt"
update="pnlIngredients"
<p:autoComplete id="rcuisine"
value="#{recipeDetailBean.recipe.cuisine}"
completeMethod="#{recipeDetailBean.cuisineSuggestions}"
/>
<p:outputLabel for="@next"
value="Add Ingredient: "
value="Rating"
/>
<p:inputTextarea
id="ctlAddIngTxt"
focus="true" rows="1"
cols="65"
value="#{recipeDetailBean.ingredientText}"
title="You can paste in multiple ingredients here!"
onkeydown="if (event.keyCode === 13) { ingButton(); this.focus(); return false; }"
<p:rating id="rrating" max="10"
value="#{recipeDetailBean.recipe.rating}"
/>
<p:commandButton id="ctlAddIng"
value="+ Add"
onclick="ingButton(); return false;"
<p:outputLabel for="@next"
value="Source"
/>
<p:inputText id="rsource"
size="45"
value="#{recipeDetailBean.recipe.source}"
/>
<p:outputLabel for="@next"
value="URL"
/>
<p:inputText id="rurl" size="45"
value="#{recipeDetailBean.recipe.link}"
/>
<p:outputLabel for="@next"
value="Description"
/>
<p:inputTextarea id="description"
rows="10" cols="45"
value="#{recipeDetailBean.recipe.description}"
/>
<p:panel id="picPanel">
<img id="bigPix"
src="/img/picture/?dt=#{recipeDetailBean.currentTime}"
/>
</p:panel>
<p:fileUpload id="ctlUpload"
label="Upload Image"
fileUploadListener="#{recipeDetailBean.ajaxUploadImage}"
global="true" mode="advanced"
multiple="false"
update=":messages picPanel"
auto="true"
sizeLimit="1000000"
allowTypes="/(\.|\/)(gif|jpe?g|png)$/"
/>
<p:commandButton id="ctlDelImg"
value="Delete Image"
>
<f:ajax
listener="#{recipeDetailBean.ajaxDeleteImage}"
render="picPanel"
immediate="true"
/>
</p:commandButton>
</h:panelGroup>
</p:panel>
</p:tab>
<p:tab id="instructionsTab"
title="Instructions"
>
<p:panel header="Instructions">
<div id="insection">
<p:textEditor
id="ctlInstructions"
</p:panelGrid>
</p:tab>
<p:tab id="ingredientsTab"
title="Ingredients"
>
<p:panel id="pnlIngredients">
<f:facet name="header">Ingredients</f:facet>
<h:panelGroup id="ingButtons">
<p:commandButton value="Up"
disabled="#{not recipeDetailBean.moveUpAble}"
id="ctlUp"
>
<f:ajax
listener="#{recipeDetailBean.ajaxMoveUp}"
execute="ingredientTable"
render="ingredientTable"
/>
</p:commandButton>
<p:commandButton value="Down"
disabled="#{not recipeDetailBean.moveDownAble}"
id="ctlDown"
>
<f:ajax
listener="#{recipeDetailBean.ajaxMoveDown}"
execute="ingredientTable"
render="ingredientTable"
/>
</p:commandButton>
<p:button value="Add Group"
disabled="true"
/>
<p:button
value="Add Recipe As Ingredient"
disabled="true"
/>
<p:button
value="Import Ingredients"
disabled="true"
/>
<p:button value="Paste" />
<p:commandButton
value="Delete"
id="ctlDelete"
disabled="not #{recipeDetailBean.selectionActive}"
>
<f:ajax
listener="#{recipeDetailBean.ajaxDeleteItems}"
immediate="true"
render="ingredientTable"
/>
</p:commandButton>
</h:panelGroup>
<p:dataTable id="ingredientTable"
style="width: 100%; height: 420px; margin-top: 8px"
value="#{recipeDetailBean.ingredients}"
scrollable="true"
scrollHeight="380" var="item"
>
<p:column label="Sel."
align="center"
style="width: 2em"
>
<p:selectBooleanCheckbox
id="selected"
value="#{item.selected}"
>
<f:ajax
immediate="true"
render="pnlIngredients"
/>
</p:selectBooleanCheckbox>
</p:column>
<p:column
style="width: 3.6em"
>
<f:facet name="header">
Amt.
</f:facet>
<p:inputText id="ingAmt"
size="3"
value="#{item.displayAmount}"
style="text-align: right"
rendered="#{not item.ingGroup}"
>
</p:inputText>
</p:column>
<p:column style="width: 7em">
<f:facet name="header">
Units
</f:facet>
<p:inputText id="ingUnit"
value="#{item.unit}"
size="10"
rendered="#{not item.ingGroup}"
>
</p:inputText>
</p:column>
<p:column style="width: 26em">
<f:facet name="header">
Item
</f:facet>
<p:inputText id="ingItem"
size="42"
value="#{item.item}"
>
</p:inputText>
</p:column>
<p:column label="Optional"
align="center"
styleClass="ingSel"
>
<f:facet name="header">
Opt.
</f:facet>
<p:selectBooleanCheckbox
id="ingOpt"
value="#{item.optionalCB}"
rendered="#{not item.ingGroup}"
/>
</p:column>
<p:column style="width: 13em">
<f:facet name="header">
Ing. Key
</f:facet>
<p:inputText id="ingKey"
value="#{item.ingkey}"
size="20"
rendered="#{not item.ingGroup}"
>
<f:ajax
event="change"
listener="#{recipeDetailBean.ajaxUpdateShopcat(item)}"
render="shopCat"
/>
</p:inputText>
</p:column>
<p:column>
<f:facet name="header">
Shop. Cat.
</f:facet>
<h:outputText
id="shopCat"
value="#{item.shopCat}"
rendered="#{not item.ingGroup}"
/>
<p:commandButton
id="eShopcat"
value="E"
action="#{recipeDetailBean.ajaxEditShopcat(item)}"
update="editShopcatDlg"
oncomplete="PF('editShopcatDlg').show()"
>
</p:commandButton>
</p:column>
</p:dataTable>
<h:panelGroup id="pnlIng">
<p:remoteCommand
name="ingButton"
action="#{recipeDetailBean.doAddIngredient}"
process="@this ctlAddIngTxt"
update="pnlIngredients"
/>
<p:outputLabel for="@next"
value="Add Ingredient: "
/>
<p:inputTextarea
id="ctlAddIngTxt"
focus="true" rows="1"
cols="65"
value="#{recipeDetailBean.ingredientText}"
title="You can paste in multiple ingredients here!"
onkeydown="if (event.keyCode === 13) { ingButton(); this.focus(); return false; }"
/>
<p:commandButton
id="ctlAddIng"
value="+ Add"
onclick="ingButton(); return false;"
>
</p:commandButton>
</h:panelGroup>
</p:panel>
</p:tab>
<p:tab id="instructionsTab"
title="Instructions"
>
<p:panel header="Instructions">
<div id="insection">
<p:textEditor
id="ctlInstructions"
height="320"
escape="false"
value="#{recipeDetailBean.recipe.instructions}"
/>
</div>
</p:panel>
</p:tab>
<p:tab id="notesTab" title="Notes">
<p:panel header="Notes">
<p:textEditor id="ctlNotes"
height="320" escape="false"
value="#{recipeDetailBean.recipe.instructions}"
value="#{recipeDetailBean.recipe.modifications}"
/>
</div>
</p:panel>
</p:tab>
<p:tab id="notesTab" title="Notes">
<p:panel header="Notes">
<p:textEditor id="ctlNotes"
height="320" escape="false"
value="#{recipeDetailBean.recipe.modifications}"
/>
</p:panel>
</p:tab>
</p:tabView>
<p:commandButton id="doSave" value="Save"
disabled="{not recipeDetailBean.dirty}"
action="#{recipeDetailBean.doSave}"
/>
<p:commandButton id="doCancel" value="Cancel"
immediate="true" action="/main.jsf"
/>
</h:form>
</p:panel>
</ui:define>
</ui:composition>
</p:panel>
</p:tab>
</p:tabView>
<p:commandButton id="doSave" value="Save"
disabled="{not recipeDetailBean.dirty}"
action="#{recipeDetailBean.doSave}"
/>
<p:commandButton id="doCancel" value="Cancel"
immediate="true" action="/main.jsf"
/>
</h:form>
</p:panel>
<p:dialog id="editShopcatDlg" widgetVar="editShopcatDlg">
<ui:include src="editShopcat.xhtml" />
</p:dialog>
</ui:define>
</ui:composition>
</body>
</html>

View File

@ -0,0 +1,61 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:p="http://primefaces.org/ui"
>
<h:head>
<title>Shopping Category</title>
<style type="text/css">
html {
font-size: 14px;
}
</style>
</h:head>
<h:body>
<ui:component>
<h:form id="frmShopcat">
<p:panelGrid columns="1">
<div>Ingredient key:
#{editShopcatBean.ingkey}
</div>
<p:outputLabel for="@next"
value="Category Name" />
<p:inputText id="ctlShopcat"
value="#{editShopcatBean.shopcatName}"
>
</p:inputText>
<h:outputText
value="suggestion:" />
<p:selectOneMenu
id="ctlShopcatMenu"
value="#{editShopcatBean.shopcatSuggestion}"
>
<f:selectItems
value="#{editShopcatBean.shopcatSuggestionList}" />
<p:ajax event="change"
listener="#{editShopcatBean.ajaxShopcatSuggest}"
update="ctlShopcat" />
</p:selectOneMenu>
<p:panelGrid columns="2"
style="width: 100%"
>
<p:commandButton
id="scDlgOK" value="OK"
style="width: 6em"
action="#{recipeDetailBean.doUpdateShopcat}"
update="form1:tabGroupClient:ingredientTable"
oncomplete="PF('editShopcatDlg').hide()" />
<p:commandButton
id="scDlgCan" value="Cancel"
style="width: 6em"
onclick="PF('editShopcatDlg').hide()" />
</p:panelGrid>
</p:panelGrid>
</h:form>
</ui:component>
</h:body>
</html>

View File

@ -0,0 +1,9 @@
<html>
<head>
<title>ERROR</title>
</head>
<body>
<h1>Uh-oh!</h1>
An error happened.
</body>
</html>