parent
851658a356
commit
4bcf8b911f
9 changed files with 1289 additions and 0 deletions
@ -0,0 +1,208 @@ |
|||||||
|
package com.mousetech.gourmetj; |
||||||
|
|
||||||
|
import java.io.Serializable; |
||||||
|
|
||||||
|
import javax.annotation.PostConstruct; |
||||||
|
|
||||||
|
import javax.faces.model.DataModel; |
||||||
|
import javax.faces.model.ListDataModel; |
||||||
|
import javax.faces.view.ViewScoped; |
||||||
|
import javax.inject.Inject; |
||||||
|
import javax.inject.Named; |
||||||
|
|
||||||
|
import org.slf4j.Logger; |
||||||
|
import org.slf4j.LoggerFactory; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
import com.mousetech.gourmetj.UserSession; |
||||||
|
import com.mousetech.gourmetj.persistence.model.Category; |
||||||
|
import com.mousetech.gourmetj.persistence.model.Recipe; |
||||||
|
import com.mousetech.gourmetj.persistence.service.RecipeService; |
||||||
|
|
||||||
|
/** |
||||||
|
* Main control panel backing bean. |
||||||
|
* |
||||||
|
* @author timh |
||||||
|
* @since Jun 28, 2012 |
||||||
|
*/ |
||||||
|
|
||||||
|
@Named |
||||||
|
@ViewScoped |
||||||
|
public class AdminMainBean implements Serializable { |
||||||
|
|
||||||
|
/** |
||||||
|
* |
||||||
|
*/ |
||||||
|
private static final long serialVersionUID = 2L; |
||||||
|
|
||||||
|
public AdminMainBean() { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/* Logger */ |
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(AdminMainBean.class); |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Persistency service for Recipes |
||||||
|
*/ |
||||||
|
|
||||||
|
@Inject |
||||||
|
transient RecipeService recipeService; |
||||||
|
|
||||||
|
public void setRecipeService(RecipeService service) { |
||||||
|
log.debug("INJECT RECIPESERVICE===" + service); |
||||||
|
this.recipeService = service; |
||||||
|
} |
||||||
|
|
||||||
|
// **
|
||||||
|
@Inject |
||||||
|
private UserSession userSession; |
||||||
|
|
||||||
|
/** |
||||||
|
* @return the userSession |
||||||
|
*/ |
||||||
|
public UserSession getUserSession() { |
||||||
|
return userSession; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param userSession the userSession to set |
||||||
|
*/ |
||||||
|
public void setUserSession(UserSession userSession) { |
||||||
|
this.userSession = userSession; |
||||||
|
} |
||||||
|
|
||||||
|
// **
|
||||||
|
private String searchText; |
||||||
|
|
||||||
|
/** |
||||||
|
* @return the searchText |
||||||
|
*/ |
||||||
|
public String getSearchText() { |
||||||
|
if (this.searchResults == null) { |
||||||
|
// Fake around broken @PostConstruct
|
||||||
|
this.setSearchText(userSession.getLastSearch()); |
||||||
|
} |
||||||
|
return searchText; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param searchText the searchText to set |
||||||
|
*/ |
||||||
|
public void setSearchText(String searchText) { |
||||||
|
this.searchText = searchText; |
||||||
|
} |
||||||
|
|
||||||
|
/**/ |
||||||
|
transient DataModel<Recipe> searchResults; |
||||||
|
|
||||||
|
/** |
||||||
|
* @return the searchResults |
||||||
|
*/ |
||||||
|
public DataModel<Recipe> getSearchResults() { |
||||||
|
if (searchResults == null) { |
||||||
|
searchResults = new ListDataModel<Recipe>(); |
||||||
|
init(); // @PostConstruct is broken
|
||||||
|
} |
||||||
|
return searchResults; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param searchResults the searchResults to set |
||||||
|
*/ |
||||||
|
public void setSearchResults( |
||||||
|
DataModel<Recipe> searchResults) { |
||||||
|
this.searchResults = searchResults; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Return to last search, if any |
||||||
|
*/ |
||||||
|
@PostConstruct |
||||||
|
void init() { |
||||||
|
this.setSearchText(userSession.getLastSearch()); |
||||||
|
// Clean up from any previous operations.
|
||||||
|
this.userSession.setRecipe(null); |
||||||
|
doFind(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Finder |
||||||
|
* |
||||||
|
* @return Navigation string |
||||||
|
*/ |
||||||
|
public String doFind() { |
||||||
|
List<Recipe> recipes = |
||||||
|
recipeService.findByTitle(searchText); |
||||||
|
|
||||||
|
getSearchResults().setWrappedData(recipes); |
||||||
|
this.userSession.setLastSearch(this.getSearchText()); |
||||||
|
return null; // Stay on page
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Prep to create a new recipe |
||||||
|
* |
||||||
|
* @return navigation to detailEdit page |
||||||
|
*/ |
||||||
|
public String doNewRecipe() { |
||||||
|
// Clear for new recipe
|
||||||
|
this.userSession.setLastEdit(null); |
||||||
|
// Construct a blank recipe to be created.
|
||||||
|
this.userSession.setRecipe(new Recipe()); |
||||||
|
return "detailEdit?faces-redirect=true"; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Show selected recipe |
||||||
|
* |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public String showRecipe() { |
||||||
|
long recipeId = getSearchResults().getRowData().getId(); |
||||||
|
JSFUtils.flashScope().put("recipeID", |
||||||
|
Long.valueOf(recipeId)); |
||||||
|
userSession.setLastEdit(recipeId); |
||||||
|
userSession.setRecipe(null); // forces loading of line
|
||||||
|
// items.
|
||||||
|
return "recipeDetails?faces-redirect=true"; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get printable preptime. Database version is in seconds. |
||||||
|
* |
||||||
|
* @return Formatted time. Called from EL on main page. |
||||||
|
*/ |
||||||
|
public String formatPreptime(int timesec) { |
||||||
|
StringBuffer sb = new StringBuffer(20); |
||||||
|
int preptime = timesec / 60; |
||||||
|
if (preptime > 60) { |
||||||
|
int hours = preptime / 60; |
||||||
|
sb.append(hours); |
||||||
|
sb.append(" h. "); |
||||||
|
preptime %= 60; |
||||||
|
} |
||||||
|
if (preptime > 0) { |
||||||
|
sb.append(preptime); |
||||||
|
sb.append(" min."); |
||||||
|
} |
||||||
|
return sb.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
public String formatCategories(Recipe r) { |
||||||
|
StringBuffer sb = new StringBuffer(30); |
||||||
|
boolean first = true; |
||||||
|
for (Category cat : r.getCategories()) { |
||||||
|
if (first) { |
||||||
|
first = false; |
||||||
|
} else { |
||||||
|
sb.append(", "); |
||||||
|
} |
||||||
|
sb.append(cat.getCategory()); |
||||||
|
} |
||||||
|
return sb.toString(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,49 @@ |
|||||||
|
/** |
||||||
|
* Copyright (C) 2021, Tim Holloway |
||||||
|
* |
||||||
|
* Date written: Dec 7, 2021 |
||||||
|
* Author: Tim Holloway <timh@mousetech.com> |
||||||
|
*/ |
||||||
|
package com.mousetech.gourmetj; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
import javax.enterprise.context.ApplicationScoped; |
||||||
|
import javax.inject.Inject; |
||||||
|
import javax.inject.Named; |
||||||
|
|
||||||
|
import com.mousetech.gourmetj.persistence.service.RecipeService; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author timh |
||||||
|
* @since Dec 7, 2021 |
||||||
|
*/ |
||||||
|
@Named |
||||||
|
@ApplicationScoped |
||||||
|
public class CuisineBean { |
||||||
|
|
||||||
|
@Inject |
||||||
|
private RecipeService recipeService; |
||||||
|
|
||||||
|
private List<String> cuisineList; |
||||||
|
|
||||||
|
/** |
||||||
|
* Return list of cuisines currently on file. Create it if |
||||||
|
* needed. |
||||||
|
*/ |
||||||
|
public List<String> getCuisineList() { |
||||||
|
if (this.cuisineList == null ) { |
||||||
|
this.cuisineList = loadCuisineList(); |
||||||
|
} |
||||||
|
return this.cuisineList; |
||||||
|
} |
||||||
|
|
||||||
|
private synchronized List<String> loadCuisineList() { |
||||||
|
List<String> list = this.recipeService.findCuisines(); |
||||||
|
return list; |
||||||
|
} |
||||||
|
|
||||||
|
public synchronized void registerCuisine(String cuisineString) { |
||||||
|
// search in-memory list. Add if needed.
|
||||||
|
} |
||||||
|
} |
@ -0,0 +1,332 @@ |
|||||||
|
package com.mousetech.gourmetj; |
||||||
|
|
||||||
|
import java.text.DecimalFormat; |
||||||
|
import java.util.regex.Matcher; |
||||||
|
import java.util.regex.Pattern; |
||||||
|
|
||||||
|
import com.mousetech.gourmetj.persistence.model.Ingredient; |
||||||
|
|
||||||
|
/** |
||||||
|
* Service for taking an ingredients line, parsing it and |
||||||
|
* returning a populated Ingredients object. Understands fraction |
||||||
|
* characters in the amounts fields. |
||||||
|
* |
||||||
|
* @author timh |
||||||
|
* @since Dec 1, 2021 TestedBy @see IngredientDigesterTest |
||||||
|
*/ |
||||||
|
public class IngredientDigester { |
||||||
|
|
||||||
|
/** |
||||||
|
* @author timh |
||||||
|
* @since Dec 5, 2021 |
||||||
|
*/ |
||||||
|
public enum IngredientAmountFormat { |
||||||
|
IA_DECIMAL, // 5.75
|
||||||
|
IA_TEXT, // 5 3/4
|
||||||
|
IA_SYMBOLS, // 5¾
|
||||||
|
} |
||||||
|
|
||||||
|
final static String amountPatternTxt = |
||||||
|
"([0-9¼⅓½⅔¾⅛⅜⅝⅞][\\s\\-\\.]*[/0-9¼⅓½⅔¾⅛⅜⅝⅞]*)"; |
||||||
|
final static Pattern amountPattern = |
||||||
|
Pattern.compile(amountPatternTxt); |
||||||
|
|
||||||
|
final static Pattern fractionPattern = |
||||||
|
Pattern.compile("(\\d*)[\\s\\-]*(\\d+)/(\\d+)"); |
||||||
|
|
||||||
|
/** |
||||||
|
* Digest an ingredient text line and construct an Ingredient |
||||||
|
* object |
||||||
|
* |
||||||
|
* @param inputText Input text line |
||||||
|
* @return Ingredient |
||||||
|
*/ |
||||||
|
public static Ingredient digest(String inputText) { |
||||||
|
inputText = inputText.trim(); |
||||||
|
final boolean optional = |
||||||
|
inputText.toLowerCase().contains("optional"); |
||||||
|
|
||||||
|
Ingredient ing = new Ingredient(); |
||||||
|
ing.setAmount(0.0); |
||||||
|
|
||||||
|
// Split for amount, unit and rest of string.
|
||||||
|
String[] pamt = parseFancyNumber(inputText); |
||||||
|
if (pamt == null) { |
||||||
|
// Can't parse
|
||||||
|
ing.setAmount(null); |
||||||
|
ing.setUnit(null); |
||||||
|
ing.setItem(inputText); |
||||||
|
} else { |
||||||
|
final Double[] amount = digestAmount(pamt[0]); |
||||||
|
ing.setAmount(amount[0]); |
||||||
|
ing.setRangeamount(amount[1]); |
||||||
|
|
||||||
|
String[] unext = pamt[1].split("\\s", 2); |
||||||
|
switch (unext.length) { |
||||||
|
case 1: |
||||||
|
ing.setUnit(null); |
||||||
|
ing.setItem(unext[0]); |
||||||
|
break; |
||||||
|
case 2: |
||||||
|
ing.setUnit(unext[0]); |
||||||
|
ing.setItem(unext[1]); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
ing.setPosition(null); |
||||||
|
ing.setOptional(optional ? 1 : 0); |
||||||
|
// Extract the item type from the item name
|
||||||
|
// Assume possible prep details ("parsley, chopped")
|
||||||
|
String istring = ing.getItem(); |
||||||
|
String iclass = istring.split("\\s*,",2)[0]; |
||||||
|
ing.setIngkey(iclass); |
||||||
|
return ing; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Break down ingredients line into 2 parts. First part is |
||||||
|
* numeric text representing amount and optional range, |
||||||
|
* second part is text remainder, including units. |
||||||
|
* |
||||||
|
* @param instring String to parse |
||||||
|
* @return 2-dimensional String array with amount and |
||||||
|
* remainder or <code>null</code> if instring does |
||||||
|
* not parse. |
||||||
|
* |
||||||
|
*/ |
||||||
|
static String[] parseFancyNumber(String instring) { |
||||||
|
Matcher m = amountPattern.matcher(instring); |
||||||
|
if (m.find()) { |
||||||
|
// System.out.println("GROUPS=" + m.groupCount());
|
||||||
|
String rs = m.group(1); |
||||||
|
return new String[] { rs.trim(), |
||||||
|
instring.substring(m.end()).trim() }; |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
// TODO: Remove "optional", if present, including
|
||||||
|
// brackets/parentheses
|
||||||
|
|
||||||
|
/** |
||||||
|
* Convert instring into a number with optional fractions. |
||||||
|
* Can take decimals, unicode ratio characters, and n/m form |
||||||
|
* with embedded spaces and dashes. |
||||||
|
* |
||||||
|
* @param instring String to analyze |
||||||
|
* @return number equivalent pair. Note that thirds are not |
||||||
|
* precise in binary floating-point! First element is |
||||||
|
* low range amount. Second element is null unless a |
||||||
|
* high range was given as well. |
||||||
|
*/ |
||||||
|
public static Double[] digestAmount(String instring) { |
||||||
|
String[] amtParts = instring.split("\\s*-\\s*"); |
||||||
|
Double[] result = new Double[2]; |
||||||
|
result[0] = parseSingleAmount(amtParts[0]); |
||||||
|
if (amtParts.length == 1) { |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
// dash separates low, high values OR integer and a
|
||||||
|
// fraction.
|
||||||
|
result[1] = parseSingleAmount(amtParts[1]); |
||||||
|
if (result[1] < result[0]) { |
||||||
|
result[0] += result[1]; |
||||||
|
result[1] = 0.0d; |
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Parse a single amount. May be integer, decimal number, |
||||||
|
* integer + fraction character or [integer] integer / |
||||||
|
* integer. |
||||||
|
* |
||||||
|
* @param instring amount string to parse |
||||||
|
* @return parsed value. |
||||||
|
* @throws NumberFormatException if bad decimal value given. |
||||||
|
*/ |
||||||
|
static Double parseSingleAmount(String instring) { |
||||||
|
double value = 0; |
||||||
|
double frac = 0; |
||||||
|
|
||||||
|
if (instring.contains("/")) { |
||||||
|
// Break down a/b fraction
|
||||||
|
frac = digestFraction(instring); |
||||||
|
return frac; |
||||||
|
} |
||||||
|
|
||||||
|
if (instring.contains(".")) { |
||||||
|
frac = Double.valueOf(instring); |
||||||
|
return frac; |
||||||
|
} |
||||||
|
|
||||||
|
// Look for integer and possibly a symbol;
|
||||||
|
for (int i = 0; i < instring.length(); i++) { |
||||||
|
final Character ch = instring.charAt(i); |
||||||
|
switch (ch) { |
||||||
|
case '⅛': |
||||||
|
frac = 0.125d; |
||||||
|
break; |
||||||
|
case '⅓': |
||||||
|
frac = 1.0 / 3.0; |
||||||
|
break; |
||||||
|
case '¼': |
||||||
|
frac = 0.25d; |
||||||
|
break; |
||||||
|
case '⅜': |
||||||
|
frac = 3.0 * 0.125d; |
||||||
|
break; |
||||||
|
case '½': |
||||||
|
frac = 0.5d; |
||||||
|
break; |
||||||
|
case '⅝': |
||||||
|
frac = 5.0 * 0.125d; |
||||||
|
break; |
||||||
|
case '⅔': |
||||||
|
frac = 2.0 / 3.0; |
||||||
|
break; |
||||||
|
case '⅞': |
||||||
|
frac = 7.0 * 0.125d; |
||||||
|
break; |
||||||
|
default: |
||||||
|
if (Character.isDigit(ch)) { |
||||||
|
value = value * 10.0 + (ch - '0'); |
||||||
|
} |
||||||
|
// Ignore spaces, dashes, etc.
|
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
value += frac; |
||||||
|
return value; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Pull fraction from tail-end of string and evaluate it. |
||||||
|
* |
||||||
|
* @param instring String in the form dddxxxddd/ddd, where d |
||||||
|
* is digit, x is dash or space (optional) |
||||||
|
* @return results of evaluation. May need to throw |
||||||
|
* Exception??? |
||||||
|
*/ |
||||||
|
static double digestFraction(String instring) { |
||||||
|
Matcher m = fractionPattern.matcher(instring); |
||||||
|
if (!m.find()) { |
||||||
|
return 0.0; |
||||||
|
} |
||||||
|
|
||||||
|
String lead = m.group(1); |
||||||
|
String snumerator = m.group(2); |
||||||
|
String sdenominator = m.group(3); |
||||||
|
|
||||||
|
double numerator; |
||||||
|
double denominator; |
||||||
|
try { |
||||||
|
numerator = Double.parseDouble(snumerator); |
||||||
|
denominator = Double.parseDouble(sdenominator); |
||||||
|
} catch (NumberFormatException e) { |
||||||
|
// TODO Auto-generated catch block
|
||||||
|
e.printStackTrace(); |
||||||
|
return 0.0; |
||||||
|
} |
||||||
|
|
||||||
|
if (denominator == 0.0d) { |
||||||
|
return 0.0d; |
||||||
|
} |
||||||
|
double result = numerator / denominator; |
||||||
|
if (!lead.isEmpty()) { |
||||||
|
result += Double.valueOf(lead); // integral part
|
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Convert floating-point amounts to display-friendly form. |
||||||
|
* |
||||||
|
* @param format Amount formatting scheme |
||||||
|
* @param amt Amount to display |
||||||
|
* @param ext amount range high value (can be null) |
||||||
|
* @return Displayable String. Zero returns an empty string. |
||||||
|
* |
||||||
|
*/ |
||||||
|
public static String displayAmount( |
||||||
|
IngredientAmountFormat format, Double amt, |
||||||
|
Double ext) { |
||||||
|
if (amt == 0.0d) { |
||||||
|
return ""; |
||||||
|
} |
||||||
|
String amountRange = displayAmountPart(format, amt); |
||||||
|
if (ext != null) { |
||||||
|
amountRange += "-" + displayAmountPart(format, ext); |
||||||
|
} |
||||||
|
return amountRange; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param format |
||||||
|
* @param amt Amount to format |
||||||
|
* @return Amount formatted in decimal, text, or text/symbol. |
||||||
|
* Values that don't conform to the common fractions |
||||||
|
* report as decimal values. Returns empty string for |
||||||
|
* 0. |
||||||
|
*/ |
||||||
|
static String displayAmountPart( |
||||||
|
IngredientAmountFormat format, Double amt) { |
||||||
|
if (amt == 0.0d) { |
||||||
|
return ""; |
||||||
|
} |
||||||
|
DecimalFormat df = new DecimalFormat("#.###"); |
||||||
|
String fnum = df.format(amt); |
||||||
|
if (format == IngredientAmountFormat.IA_DECIMAL) { |
||||||
|
return fnum; |
||||||
|
} |
||||||
|
|
||||||
|
String part[] = fnum.split("\\."); |
||||||
|
if (part.length == 1) { |
||||||
|
return part[0]; // Integer
|
||||||
|
} |
||||||
|
String p2 = part[1]; |
||||||
|
String fs = ""; |
||||||
|
if (format == IngredientAmountFormat.IA_TEXT) { |
||||||
|
if (p2.equals("125")) { |
||||||
|
fs = "1/8"; |
||||||
|
} else if (p2.equals("333")) { |
||||||
|
fs = "1/3"; |
||||||
|
} else if (p2.equals("25")) { |
||||||
|
fs = "1/4"; |
||||||
|
} else if (p2.equals("5")) { |
||||||
|
fs = "1/2"; |
||||||
|
} else if (p2.equals("667")) { |
||||||
|
fs = "2/3"; |
||||||
|
} else if (p2.equals("75")) { |
||||||
|
fs = "3/4"; |
||||||
|
} else { // 3, 5, 7/8
|
||||||
|
return fnum; |
||||||
|
} |
||||||
|
if (!"0".equals(part[0])) { |
||||||
|
fs = ' ' + fs; |
||||||
|
} |
||||||
|
} else { |
||||||
|
if (p2.equals("125")) { |
||||||
|
fs = "⅛"; |
||||||
|
} else if (p2.equals("333")) { |
||||||
|
fs = "⅓"; |
||||||
|
} else if (p2.equals("25")) { |
||||||
|
fs = "¼"; |
||||||
|
} else if (p2.equals("5")) { |
||||||
|
fs = "½"; |
||||||
|
} else if (p2.equals("667")) { |
||||||
|
fs = "⅔"; |
||||||
|
} else if (p2.equals("75")) { |
||||||
|
fs = "¾"; |
||||||
|
} else { // 3, 5, 7/8
|
||||||
|
return fnum; |
||||||
|
} |
||||||
|
} |
||||||
|
if ("0".equals(part[0])) { |
||||||
|
part[0] = ""; |
||||||
|
} |
||||||
|
return part[0] + fs; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,375 @@ |
|||||||
|
package com.mousetech.gourmetj; |
||||||
|
|
||||||
|
|
||||||
|
import com.mousetech.gourmetj.persistence.model.Ingredient; |
||||||
|
import com.mousetech.gourmetj.persistence.model.IngredientIF; |
||||||
|
import com.mousetech.gourmetj.persistence.model.Recipe; |
||||||
|
import com.mousetech.gourmetj.IngredientDigester; |
||||||
|
import com.mousetech.gourmetj.IngredientDigester.IngredientAmountFormat; |
||||||
|
|
||||||
|
/** |
||||||
|
* JSF-friendly decorator for @see Ingredient. Formats amount |
||||||
|
* with fractions and supports checkboxes. Primary use |
||||||
|
* is as a JSF TableModel wrapped content in @see RecipeDetailBean. |
||||||
|
* |
||||||
|
* TestedBy @see IngredientUITest |
||||||
|
* |
||||||
|
* @author timh |
||||||
|
* @since Dec 2, 2021 |
||||||
|
*/ |
||||||
|
public class IngredientUI implements IngredientIF { |
||||||
|
private Ingredient ingredient; |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. |
||||||
|
* @param ingredient |
||||||
|
*/ |
||||||
|
public IngredientUI(Ingredient ingredient) { |
||||||
|
this.ingredient = ingredient; |
||||||
|
} |
||||||
|
|
||||||
|
public Ingredient getIngredient() { |
||||||
|
return this.ingredient; |
||||||
|
} |
||||||
|
|
||||||
|
private boolean ingGroup; |
||||||
|
/** |
||||||
|
* @param ingGroup the ingGroup to set |
||||||
|
*/ |
||||||
|
public void setIngGroup(boolean ingGroup) { |
||||||
|
this.ingGroup = ingGroup; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Ingredient groups are rendered visually as synthetic |
||||||
|
* rows. Actual group IDs are part of line-item ingredients, |
||||||
|
* so when building the model, we create a group row when |
||||||
|
* the line item inggroup value changes. |
||||||
|
* |
||||||
|
* @return <code>true</code> for an Ingredient Group |
||||||
|
* header row. |
||||||
|
*/ |
||||||
|
public boolean isIngGroup() { |
||||||
|
return this.ingGroup; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Row selection checkbox (UI only) |
||||||
|
*/ |
||||||
|
|
||||||
|
private boolean selected; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* @return the selected status |
||||||
|
*/ |
||||||
|
public boolean isSelected() { |
||||||
|
return selected; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param selected the selected to set |
||||||
|
*/ |
||||||
|
public void setSelected(boolean selected) { |
||||||
|
this.selected = selected; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* @return |
||||||
|
* @see com.mousetech.gourmetj.persistence.model.Ingredient#getAmount() |
||||||
|
*/ |
||||||
|
public Double getAmount() { |
||||||
|
return ingredient.getAmount(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param amount |
||||||
|
* @see com.mousetech.gourmetj.persistence.model.Ingredient#setAmount(java.lang.Double) |
||||||
|
*/ |
||||||
|
public void setAmount(Double amount) { |
||||||
|
ingredient.setAmount(amount); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get amount display-friendly |
||||||
|
* @see #getAmount |
||||||
|
*/ |
||||||
|
public String getDisplayAmount() { |
||||||
|
// TODO
|
||||||
|
Double amt = ingredient.getAmount(); |
||||||
|
if ( amt == null ) { |
||||||
|
return ""; |
||||||
|
} |
||||||
|
String amt1 = IngredientDigester.displayAmount( |
||||||
|
IngredientAmountFormat.IA_SYMBOLS, amt, ingredient.getRangeamount()); |
||||||
|
return amt1; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set amount display-friendly |
||||||
|
* @see #setAmount |
||||||
|
*/ |
||||||
|
public void setDisplayAmount(String amount) { |
||||||
|
if ( amount.isBlank() ) { |
||||||
|
ingredient.setAmount(null); |
||||||
|
} |
||||||
|
IngredientDigester digester = new IngredientDigester(); |
||||||
|
Double[] amt = digester.digestAmount(amount); |
||||||
|
ingredient.setAmount(amt[0]); |
||||||
|
ingredient.setRangeamount(amt[1]); |
||||||
|
} |
||||||
|
/** |
||||||
|
* @return |
||||||
|
* @see com.mousetech.gourmetj.persistence.model.Ingredient#getDeleted() |
||||||
|
*/ |
||||||
|
public Integer getDeleted() { |
||||||
|
return ingredient.getDeleted(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param deleted |
||||||
|
* @see com.mousetech.gourmetj.persistence.model.Ingredient#setDeleted(java.lang.Integer) |
||||||
|
*/ |
||||||
|
public void setDeleted(Integer deleted) { |
||||||
|
ingredient.setDeleted(deleted); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return |
||||||
|
* @see com.mousetech.gourmetj.persistence.model.Ingredient#getId() |
||||||
|
*/ |
||||||
|
public Long getId() { |
||||||
|
return ingredient.getId(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param id |
||||||
|
* @see com.mousetech.gourmetj.persistence.model.Ingredient#setId(long) |
||||||
|
*/ |
||||||
|
public void setId(Long id) { |
||||||
|
ingredient.setId(id); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return |
||||||
|
* @see java.lang.Object#hashCode() |
||||||
|
*/ |
||||||
|
public int hashCode() { |
||||||
|
return ingredient.hashCode(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return |
||||||
|
* @see com.mousetech.gourmetj.persistence.model.Ingredient#getInggroup() |
||||||
|
*/ |
||||||
|
public String getInggroup() { |
||||||
|
return ingredient.getInggroup(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param inggroup |
||||||
|
* @see com.mousetech.gourmetj.persistence.model.Ingredient#setInggroup(java.lang.String) |
||||||
|
*/ |
||||||
|
public void setInggroup(String inggroup) { |
||||||
|
ingredient.setInggroup(inggroup); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return |
||||||
|
* @see com.mousetech.gourmetj.persistence.model.Ingredient#getIngkey() |
||||||
|
*/ |
||||||
|
public String getIngkey() { |
||||||
|
return ingredient.getIngkey(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param ingkey |
||||||
|
* @see com.mousetech.gourmetj.persistence.model.Ingredient#setIngkey(java.lang.String) |
||||||
|
*/ |
||||||
|
public void setIngkey(String ingkey) { |
||||||
|
ingredient.setIngkey(ingkey); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return |
||||||
|
* @see com.mousetech.gourmetj.persistence.model.Ingredient#getItem() |
||||||
|
*/ |
||||||
|
public String getItem() { |
||||||
|
return ingredient.getItem(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param item |
||||||
|
* @see com.mousetech.gourmetj.persistence.model.Ingredient#setItem(java.lang.String) |
||||||
|
*/ |
||||||
|
public void setItem(String item) { |
||||||
|
ingredient.setItem(item); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return |
||||||
|
* @see com.mousetech.gourmetj.persistence.model.Ingredient#getOptional() |
||||||
|
*/ |
||||||
|
public Integer getOptional() { |
||||||
|
return ingredient.getOptional(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param optional |
||||||
|
* @see com.mousetech.gourmetj.persistence.model.Ingredient#setOptional(java.lang.Integer) |
||||||
|
*/ |
||||||
|
public void setOptional(Integer optional) { |
||||||
|
ingredient.setOptional(optional); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get optional value in boolean Checkbox friendly form |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public boolean getOptionalCB() { |
||||||
|
return ingredient.getOptional() != 0; |
||||||
|
} |
||||||
|
|
||||||
|
public void setOptionalCB(boolean value) { |
||||||
|
ingredient.setOptional(value ? 1 : 0); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return |
||||||
|
* @see com.mousetech.gourmetj.persistence.model.Ingredient#getPosition() |
||||||
|
*/ |
||||||
|
public Integer getPosition() { |
||||||
|
return ingredient.getPosition(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param position |
||||||
|
* @see com.mousetech.gourmetj.persistence.model.Ingredient#setPosition(java.lang.Integer) |
||||||
|
*/ |
||||||
|
public void setPosition(Integer position) { |
||||||
|
ingredient.setPosition(position); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return |
||||||
|
* @see com.mousetech.gourmetj.persistence.model.Ingredient#getRangeamount() |
||||||
|
*/ |
||||||
|
public Double getRangeamount() { |
||||||
|
return ingredient.getRangeamount(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param rangeamount |
||||||
|
* @see com.mousetech.gourmetj.persistence.model.Ingredient#setRangeamount(java.lang.Double) |
||||||
|
*/ |
||||||
|
public void setRangeamount(Double rangeamount) { |
||||||
|
ingredient.setRangeamount(rangeamount); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return |
||||||
|
* @see com.mousetech.gourmetj.persistence.model.Ingredient#getRecipe() |
||||||
|
*/ |
||||||
|
public Recipe getRecipe() { |
||||||
|
return ingredient.getRecipe(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param recipe |
||||||
|
* @see com.mousetech.gourmetj.persistence.model.Ingredient#setRecipe(com.mousetech.gourmetj.persistence.model.Recipe) |
||||||
|
*/ |
||||||
|
public void setRecipe(Recipe recipe) { |
||||||
|
ingredient.setRecipe(recipe); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return |
||||||
|
* @see com.mousetech.gourmetj.persistence.model.Ingredient#getRefid() |
||||||
|
*/ |
||||||
|
public Long getRefid() { |
||||||
|
return ingredient.getRefid(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param refid |
||||||
|
* @see com.mousetech.gourmetj.persistence.model.Ingredient#setRefid(java.lang.Long) |
||||||
|
*/ |
||||||
|
public void setRefid(Long refid) { |
||||||
|
ingredient.setRefid(refid); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return |
||||||
|
* @see com.mousetech.gourmetj.persistence.model.Ingredient#getShopoptional() |
||||||
|
*/ |
||||||
|
public Integer getShopoptional() { |
||||||
|
return ingredient.getShopoptional(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param shopoptional |
||||||
|
* @see com.mousetech.gourmetj.persistence.model.Ingredient#setShopoptional(java.lang.Integer) |
||||||
|
*/ |
||||||
|
public void setShopoptional(Integer shopoptional) { |
||||||
|
ingredient.setShopoptional(shopoptional); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return |
||||||
|
* @see com.mousetech.gourmetj.persistence.model.Ingredient#getShopoptional() |
||||||
|
*/ |
||||||
|
public boolean getShopoptionalCB() { |
||||||
|
return ingredient.getShopoptional() != 0; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param shopoptional |
||||||
|
* @see com.mousetech.gourmetj.persistence.model.Ingredient#setShopoptional(java.lang.Integer) |
||||||
|
*/ |
||||||
|
public void setShopoptionalCB(boolean shopoptional) { |
||||||
|
ingredient.setShopoptional(shopoptional? 1:0); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return |
||||||
|
* @see com.mousetech.gourmetj.persistence.model.Ingredient#getUnit() |
||||||
|
*/ |
||||||
|
public String getUnit() { |
||||||
|
return ingredient.getUnit(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param unit |
||||||
|
* @see com.mousetech.gourmetj.persistence.model.Ingredient#setUnit(java.lang.String) |
||||||
|
*/ |
||||||
|
public void setUnit(String unit) { |
||||||
|
ingredient.setUnit(unit); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
// This goes to the shopCats table via ManyToOne at save time.
|
||||||
|
private String shopCat; |
||||||
|
|
||||||
|
/** |
||||||
|
* @return the shopCat |
||||||
|
*/ |
||||||
|
public String getShopCat() { |
||||||
|
return shopCat; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param shopCat the shopCat to set |
||||||
|
*/ |
||||||
|
public void setShopCat(String shopCat) { |
||||||
|
this.shopCat = shopCat; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return |
||||||
|
* @see java.lang.Object#toString() |
||||||
|
*/ |
||||||
|
public String toString() { |
||||||
|
return ingredient.toString(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,86 @@ |
|||||||
|
package com.mousetech.gourmetj; |
||||||
|
|
||||||
|
import java.io.InputStream; |
||||||
|
|
||||||
|
import javax.faces.application.FacesMessage; |
||||||
|
import javax.faces.context.ExternalContext; |
||||||
|
import javax.faces.context.FacesContext; |
||||||
|
import javax.faces.context.Flash; |
||||||
|
|
||||||
|
import org.slf4j.Logger; |
||||||
|
import org.slf4j.LoggerFactory; |
||||||
|
|
||||||
|
public class JSFUtils { |
||||||
|
|
||||||
|
/* Logger */ |
||||||
|
|
||||||
|
private static final Logger log = |
||||||
|
LoggerFactory.getLogger(JSFUtils.class); |
||||||
|
|
||||||
|
private static ExternalContext getExternalContext() { |
||||||
|
FacesContext facesContext = |
||||||
|
FacesContext.getCurrentInstance(); |
||||||
|
ExternalContext externalContext = |
||||||
|
facesContext.getExternalContext(); |
||||||
|
return externalContext; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Obtain resource as Stream. The caller must close this |
||||||
|
* stream after use. |
||||||
|
* |
||||||
|
* @param pathName Path of the resource. For example, |
||||||
|
* "/WEB-INF/classes/xys.properties". |
||||||
|
* @return InputStream or <code>null</code>, if no such |
||||||
|
* resource exists. |
||||||
|
*/ |
||||||
|
public static InputStream getResourceStream( |
||||||
|
String pathName) { |
||||||
|
InputStream response = getExternalContext() |
||||||
|
.getResourceAsStream(pathName); |
||||||
|
return response; |
||||||
|
} |
||||||
|
|
||||||
|
// ===
|
||||||
|
/** |
||||||
|
* Post an info-level message to the FacesContext where the |
||||||
|
* <h:messages> tag can display it. |
||||||
|
* |
||||||
|
* @param string Message text |
||||||
|
*/ |
||||||
|
public static void addInfoMessage(String string) { |
||||||
|
FacesMessage message = new FacesMessage( |
||||||
|
FacesMessage.SEVERITY_INFO, string, string); |
||||||
|
FacesContext.getCurrentInstance().addMessage(null, |
||||||
|
message); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Post an error-level message to the FacesContext where the |
||||||
|
* <h:messages> tag can display it. |
||||||
|
* |
||||||
|
* @param string Message text |
||||||
|
*/ |
||||||
|
public static void addErrorMessage(String string) { |
||||||
|
FacesMessage message = new FacesMessage( |
||||||
|
FacesMessage.SEVERITY_ERROR, string, string); |
||||||
|
FacesContext.getCurrentInstance().addMessage(null, |
||||||
|
message); |
||||||
|
} |
||||||
|
|
||||||
|
public static void addErrorMessage(String string, |
||||||
|
Exception e) { |
||||||
|
if (e instanceof NullPointerException) { |
||||||
|
addErrorMessage( |
||||||
|
"Internal logic error (NullPointerException)"); |
||||||
|
} else { |
||||||
|
addErrorMessage(string); |
||||||
|
} |
||||||
|
log.error(string, e); |
||||||
|
} |
||||||
|
|
||||||
|
public static Flash flashScope() { |
||||||
|
return (FacesContext.getCurrentInstance() |
||||||
|
.getExternalContext().getFlash()); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,97 @@ |
|||||||
|
package com.mousetech.gourmetj; |
||||||
|
|
||||||
|
import java.io.Serializable; |
||||||
|
|
||||||
|
import javax.enterprise.context.SessionScoped; |
||||||
|
import javax.inject.Named; |
||||||
|
|
||||||
|
import com.mousetech.gourmetj.persistence.model.Recipe; |
||||||
|
|
||||||
|
@Named |
||||||
|
@SessionScoped |
||||||
|
public class UserSession implements Serializable { |
||||||
|
|
||||||
|
/** |
||||||
|
* Serial version for session save/restore |
||||||
|
*/ |
||||||
|
private static final long serialVersionUID = |
||||||
|
7449440266704831598L; |
||||||
|
|
||||||
|
private String lastSearch = ""; |
||||||
|
|
||||||
|
/** |
||||||
|
* @return the lastSearch |
||||||
|
*/ |
||||||
|
public String getLastSearch() { |
||||||
|
return lastSearch; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param lastSearch the lastSearch to set |
||||||
|
*/ |
||||||
|
public void setLastSearch(String lastSearch) { |
||||||
|
this.lastSearch = lastSearch; |
||||||
|
} |
||||||
|
|
||||||
|
private Long lastEdit; |
||||||
|
|
||||||
|
/** |
||||||
|
* @return the lastEdit |
||||||
|
*/ |
||||||
|
public Long getLastEdit() { |
||||||
|
return lastEdit; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param lastEdit the lastEdit to set |
||||||
|
*/ |
||||||
|
public void setLastEdit(Long lastEdit) { |
||||||
|
this.lastEdit = lastEdit; |
||||||
|
} |
||||||
|
|
||||||
|
//***
|
||||||
|
/** |
||||||
|
* Tab index to select when presenting editDetails. |
||||||
|
* First tab is 0. |
||||||
|
*/ |
||||||
|
private int detailTab; |
||||||
|
|
||||||
|
/** |
||||||
|
* @return the detailTab |
||||||
|
*/ |
||||||
|
public int getDetailTab() { |
||||||
|
return detailTab; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param detailTab the detailTab to set |
||||||
|
*/ |
||||||
|
public void setDetailTab(int detailTab) { |
||||||
|
this.detailTab = detailTab; |
||||||
|
} |
||||||
|
|
||||||
|
//***
|
||||||
|
private Recipe recipe; |
||||||
|
|
||||||
|
/** |
||||||
|
* Recipe is set by the mainpage bean to a blank recipe |
||||||
|
* before dispatching to the detailEdit page (new recipe). |
||||||
|
* It is also set by the detail view page so that the |
||||||
|
* detail view can be edited. |
||||||
|
* |
||||||
|
* In addition to detail editing, it's also used by the |
||||||
|
* @see PictureController. |
||||||
|
* |
||||||
|
* @return Recipe selected. |
||||||
|
*/ |
||||||
|
public Recipe getRecipe() { |
||||||
|
return recipe; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param recipe the recipe to set |
||||||
|
*/ |
||||||
|
public void setRecipe(Recipe recipe) { |
||||||
|
this.recipe = recipe; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,47 @@ |
|||||||
|
package com.mousetech.gourmetj.persistence.service; |
||||||
|
|
||||||
|
import java.io.Serializable; |
||||||
|
import java.util.List; |
||||||
|
import javax.enterprise.context.ApplicationScoped; |
||||||
|
import javax.inject.Inject; |
||||||
|
import javax.inject.Named; |
||||||
|
|
||||||
|
import org.springframework.transaction.annotation.Transactional; |
||||||
|
|
||||||
|
import com.mousetech.gourmetj.persistence.dao.CategoryRepository; |
||||||
|
import com.mousetech.gourmetj.persistence.dao.RecipeRepository; |
||||||
|
import com.mousetech.gourmetj.persistence.model.Category; |
||||||
|
import com.mousetech.gourmetj.persistence.model.Recipe; |
||||||
|
|
||||||
|
@Named |
||||||
|
@ApplicationScoped |
||||||
|
@Transactional |
||||||
|
public class RecipeService implements Serializable { |
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L; |
||||||
|
|
||||||
|
@Inject |
||||||
|
private RecipeRepository recipeRepository; |
||||||
|
|
||||||
|
public List<Recipe> findAll() { |
||||||
|
return recipeRepository.findAll(); |
||||||
|
} |
||||||
|
|
||||||
|
public List<String> findCuisines() { |
||||||
|
// TODO Auto-generated method stub
|
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
public List<Recipe> findByTitle(String searchText) { |
||||||
|
// TODO Auto-generated method stub
|
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
// /**
|
||||||
|
// *
|
||||||
|
// * @return All Category names that are not null/blank, sorted.
|
||||||
|
// */
|
||||||
|
// public List<String> findCategoryNames() {
|
||||||
|
// return categoryRepository.findDistinctCategoryNative();
|
||||||
|
// }
|
||||||
|
} |
@ -0,0 +1,67 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||||
|
<ui:composition template="/WEB-INF/layout/layout.xhtml" |
||||||
|
xmlns:f="http://xmlns.jcp.org/jsf/core" |
||||||
|
xmlns:ui="http://java.sun.com/jsf/facelets" |
||||||
|
xmlns:tc="http://myfaces.apache.org/tobago/component" |
||||||
|
> |
||||||
|
<ui:define name="title">Gourmet Recipe Manager</ui:define> |
||||||
|
<ui:define name="content"> |
||||||
|
<tc:messages /> |
||||||
|
<tc:form id="form1"> |
||||||
|
<tc:section label="Find a Recipe" level="2"> |
||||||
|
<tc:gridLayout id="gfinder" |
||||||
|
columns="60em auto auto" |
||||||
|
> |
||||||
|
<tc:in id="searchFor" focus="true" |
||||||
|
placeholder="Recipe title (todo) cuisine, etc." |
||||||
|
value="#{adminMainBean.searchText}" |
||||||
|
/> |
||||||
|
<tc:button id="find" label="Find" |
||||||
|
defaultCommand="true" |
||||||
|
action="#{adminMainBean.doFind}" |
||||||
|
> |
||||||
|
<tc:style minWidth="58px" height="40px" /> |
||||||
|
</tc:button> |
||||||
|
<tc:button label="New Recipe" |
||||||
|
action="#{adminMainBean.doNewRecipe}" |
||||||
|
> |
||||||
|
<tc:style minWidth="58px" height="40px" /> |
||||||
|
</tc:button> |
||||||
|
</tc:gridLayout> |
||||||
|
</tc:section> |
||||||
|
</tc:form> |
||||||
|
<tc:sheet id="table1" rows="30" |
||||||
|
value="#{adminMainBean.searchResults}" var="row" |
||||||
|
> |
||||||
|
<tc:column label="Icon"> |
||||||
|
<tc:image width="64" height="64" |
||||||
|
value="/gourmetj/img/thumb/#{row.id}" |
||||||
|
/> |
||||||
|
</tc:column> |
||||||
|
<tc:column label="Recipe"> |
||||||
|
<tc:link action="#{adminMainBean.showRecipe}" |
||||||
|
label="#{row.title}" |
||||||
|
/> |
||||||
|
</tc:column> |
||||||
|
<tc:column label="Category"> |
||||||
|
<tc:out |
||||||
|
value="#{adminMainBean.formatCategories(row)}" |
||||||
|
/> |
||||||
|
</tc:column> |
||||||
|
<tc:column label="Cuisine"> |
||||||
|
<tc:out value="#{row.cuisine}" /> |
||||||
|
</tc:column> |
||||||
|
<tc:column label="Rating"> |
||||||
|
<tc:stars value="#{row.rating}" readonly="true" /> |
||||||
|
</tc:column> |
||||||
|
<tc:column label="Source"> |
||||||
|
<tc:out value="#{row.source}" /> |
||||||
|
</tc:column> |
||||||
|
<tc:column label="Prep Time"> |
||||||
|
<tc:out |
||||||
|
value="#{adminMainBean.formatPreptime(row.preptime)}" |
||||||
|
/> |
||||||
|
</tc:column> |
||||||
|
</tc:sheet> |
||||||
|
</ui:define> |
||||||
|
</ui:composition> |
@ -0,0 +1,28 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||||
|
<ui:composition 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" |
||||||
|
> |
||||||
|
<f:view> |
||||||
|
<tc:page id="page"> |
||||||
|
<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" |
||||||
|
/> |
||||||
|
<tc:style file="css/style.css" /> |
||||||
|
<h1> |
||||||
|
<ui:insert name="title">Gourmet Recipe Manager (web version)</ui:insert> |
||||||
|
</h1> |
||||||
|
<ui:insert name="content"> |
||||||
|
<ui:include src="content.xhtml" /> |
||||||
|
</ui:insert> |
||||||
|
<tc:footer fixed="true"> |
||||||
|
© 2021 Tim Holloway, Licensed under the <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> |
||||||
|
</tc:footer> |
||||||
|
</tc:page> |
||||||
|
</f:view> |
||||||
|
</ui:composition> |
Loading…
Reference in new issue