Trauma averted - beans migrated
This commit is contained in:
parent
851658a356
commit
4bcf8b911f
208
src/main/java/com/mousetech/gourmetj/AdminMainBean.java
Normal file
208
src/main/java/com/mousetech/gourmetj/AdminMainBean.java
Normal file
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
49
src/main/java/com/mousetech/gourmetj/CuisineBean.java
Normal file
49
src/main/java/com/mousetech/gourmetj/CuisineBean.java
Normal file
|
@ -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.
|
||||||
|
}
|
||||||
|
}
|
332
src/main/java/com/mousetech/gourmetj/IngredientDigester.java
Normal file
332
src/main/java/com/mousetech/gourmetj/IngredientDigester.java
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
375
src/main/java/com/mousetech/gourmetj/IngredientUI.java
Normal file
375
src/main/java/com/mousetech/gourmetj/IngredientUI.java
Normal file
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
86
src/main/java/com/mousetech/gourmetj/JSFUtils.java
Normal file
86
src/main/java/com/mousetech/gourmetj/JSFUtils.java
Normal file
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
97
src/main/java/com/mousetech/gourmetj/UserSession.java
Normal file
97
src/main/java/com/mousetech/gourmetj/UserSession.java
Normal file
|
@ -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();
|
||||||
|
// }
|
||||||
|
}
|
67
src/main/resources/META-INF/resources/main.xhtml
Normal file
67
src/main/resources/META-INF/resources/main.xhtml
Normal file
|
@ -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>
|
28
src/main/resources/WEB-INF/layout/layout.xhtml
Normal file
28
src/main/resources/WEB-INF/layout/layout.xhtml
Normal file
|
@ -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
Block a user