Compare commits
3 Commits
8bcd8cb9a1
...
68462514db
Author | SHA1 | Date | |
---|---|---|---|
68462514db | |||
61942f547a | |||
9cf0f89e76 |
12
README.md
12
README.md
|
@ -1,4 +1,4 @@
|
|||
# Gourmet Recipe Manager - Spring Boot
|
||||
# Gourmet Recipe Manager - Spring Boot - Version 2
|
||||
|
||||
This is a port of Thomas Hinkle (thinkle) Gourmet Recipe Manager.
|
||||
|
||||
|
@ -86,3 +86,13 @@ employed when you run this app on your local desktop.
|
|||
|
||||
A lot of recipe websites publish images in webp form. Support
|
||||
for webp has now been added.
|
||||
|
||||
### Better session management
|
||||
|
||||
JSF tends to depend on session-scope context. Sessions, however
|
||||
time out and this has been an annoyance when a recipe is being
|
||||
displayed. To minimize this, better timeout mechanisms have been
|
||||
installed and the recipe browser keeps last-search and search-type
|
||||
values in long-lived cookies on the client. The server will read
|
||||
and cache them, but if the server times out, it will automatically
|
||||
re-read the cookies on the next request.
|
|
@ -22,3 +22,5 @@ spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
|
|||
# My special properties
|
||||
gourmet.password.file=${user.home}/.gourmetpw
|
||||
|
||||
# This will override aplication.yml
|
||||
#server.servlet.context-parameters.primefaces.THEME=le-frog
|
||||
|
|
6
pom.xml
6
pom.xml
|
@ -78,6 +78,12 @@
|
|||
<artifactId>all-themes</artifactId>
|
||||
<version>1.0.10</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<!-- Primefaces theme won't work without this! -->
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<!-- <dependency>
|
||||
<groupId>javax.enterprise</groupId>
|
||||
<artifactId>cdi-api</artifactId>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.mousetech.gourmetj;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import jakarta.faces.event.AjaxBehaviorEvent;
|
||||
|
@ -14,7 +15,9 @@ import org.slf4j.Logger;
|
|||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.mousetech.gourmetj.persistence.model.Recipe;
|
||||
import com.mousetech.gourmetj.persistence.service.RecipeService;
|
||||
|
@ -44,8 +47,11 @@ public class AdminMainBean implements Serializable {
|
|||
private static final Logger log =
|
||||
LoggerFactory.getLogger(AdminMainBean.class);
|
||||
|
||||
/** Cookie delimiter */
|
||||
private static final String CKDLM = ",";
|
||||
|
||||
/**
|
||||
* Persistency service for Recipes
|
||||
* Persistency service for Recipes.
|
||||
*/
|
||||
|
||||
@Inject
|
||||
|
@ -56,6 +62,24 @@ public class AdminMainBean implements Serializable {
|
|||
this.recipeService = service;
|
||||
}
|
||||
|
||||
// **
|
||||
@Inject
|
||||
private CookieBean cookieBean;
|
||||
|
||||
/**
|
||||
* @return the cookieBean
|
||||
*/
|
||||
public CookieBean getCookieBean() {
|
||||
return cookieBean;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param cookieBean the cookieBean to set
|
||||
*/
|
||||
public void setCookieBean(CookieBean cookieBean) {
|
||||
this.cookieBean = cookieBean;
|
||||
}
|
||||
|
||||
// **
|
||||
@Inject
|
||||
private UserSession userSession;
|
||||
|
@ -81,10 +105,10 @@ public class AdminMainBean implements Serializable {
|
|||
* @return the searchText
|
||||
*/
|
||||
public String getSearchText() {
|
||||
if (this.searchResults == null) {
|
||||
// Fake around broken @PostConstruct
|
||||
this.setSearchText(userSession.getLastSearch());
|
||||
}
|
||||
// if (this.searchResults == null) {
|
||||
// this.setSearchText(cookieBean.getSearchText());
|
||||
// }
|
||||
this.searchText = cookieBean.getSearchText();
|
||||
return searchText;
|
||||
}
|
||||
|
||||
|
@ -93,7 +117,7 @@ public class AdminMainBean implements Serializable {
|
|||
*/
|
||||
public void setSearchText(String searchText) {
|
||||
this.searchText = searchText;
|
||||
userSession.setLastSearch(searchText);
|
||||
cookieBean.setSearchText(searchText);
|
||||
}
|
||||
|
||||
private List<String> suggestionList = null;
|
||||
|
@ -104,7 +128,7 @@ public class AdminMainBean implements Serializable {
|
|||
|
||||
public List<String> searchSuggestionList(String query) {
|
||||
if (suggestionList == null) {
|
||||
switch (this.userSession.getSearchType()) {
|
||||
switch (searchtypeEnum()) {
|
||||
case rst_BY_CATEGORY:
|
||||
suggestionList = recipeService.findCategories();
|
||||
break;
|
||||
|
@ -118,6 +142,16 @@ public class AdminMainBean implements Serializable {
|
|||
return suggestionList;
|
||||
}
|
||||
|
||||
private RecipeSearchType searchtypeEnum() {
|
||||
int stn = cookieBean.getSearchType();
|
||||
return searchtypeEnum(stn);
|
||||
}
|
||||
|
||||
private RecipeSearchType searchtypeEnum(int stn) {
|
||||
RecipeSearchType st = RecipeSearchType.values()[stn];
|
||||
return st;
|
||||
}
|
||||
|
||||
/**/
|
||||
transient DataModel<Recipe> searchResults;
|
||||
|
||||
|
@ -127,7 +161,7 @@ public class AdminMainBean implements Serializable {
|
|||
public DataModel<Recipe> getSearchResults() {
|
||||
if (searchResults == null) {
|
||||
searchResults = new ListDataModel<Recipe>();
|
||||
init(); // @PostConstruct is broken
|
||||
init(); // @PostConstruct is broken TODO: fixed??
|
||||
}
|
||||
return searchResults;
|
||||
}
|
||||
|
@ -147,7 +181,7 @@ public class AdminMainBean implements Serializable {
|
|||
@PostConstruct
|
||||
void init() {
|
||||
log.debug("Initializing AdminMainBean " + this);
|
||||
this.setSearchText(userSession.getLastSearch());
|
||||
this.setSearchText(cookieBean.getSearchText());
|
||||
// Clean up from any previous operations.
|
||||
this.userSession.setRecipe(null);
|
||||
doFind();
|
||||
|
@ -177,9 +211,20 @@ public class AdminMainBean implements Serializable {
|
|||
*/
|
||||
public String doFind() {
|
||||
List<Recipe> recipes = null;
|
||||
if ( searchText == null ) {
|
||||
setSearchText("");
|
||||
}
|
||||
searchText = searchText.trim();
|
||||
|
||||
switch (this.getUserSession().getSearchType()) {
|
||||
// Persist current settings
|
||||
try {
|
||||
cookieBean.saveCookies();
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
// Something is really wrong if we can't create UTF-8!
|
||||
log.error("Unable to save cookies!", e);
|
||||
}
|
||||
RecipeSearchType st = searchtypeEnum();
|
||||
switch (st) {
|
||||
case rst_BY_NAME:
|
||||
recipes = recipeService.findByTitle(searchText);
|
||||
break;
|
||||
|
@ -197,12 +242,11 @@ public class AdminMainBean implements Serializable {
|
|||
break;
|
||||
default:
|
||||
log.error("Invalid recipe search type: "
|
||||
+ this.getUserSession().getSearchType());
|
||||
+ st);
|
||||
break;
|
||||
}
|
||||
|
||||
getSearchResults().setWrappedData(recipes);
|
||||
this.userSession.setLastSearch(this.getSearchText());
|
||||
return null; // Stay on page
|
||||
}
|
||||
|
||||
|
@ -243,27 +287,4 @@ public class AdminMainBean implements Serializable {
|
|||
// items.
|
||||
return "recipeDetails?faces-redirect=true";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get printable preptime. Database version is in seconds.
|
||||
*
|
||||
* @deprecated User {@link UserSession#formatTime(Long)}
|
||||
*
|
||||
* @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();
|
||||
}
|
||||
}
|
||||
|
|
51
src/main/java/com/mousetech/gourmetj/AppBean.java
Normal file
51
src/main/java/com/mousetech/gourmetj/AppBean.java
Normal file
|
@ -0,0 +1,51 @@
|
|||
package com.mousetech.gourmetj;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.faces.model.SelectItem;
|
||||
import jakarta.inject.Named;
|
||||
|
||||
/**
|
||||
* Appplication-scope data (mostly constants)
|
||||
*
|
||||
* @author timh
|
||||
* @since Feb 1, 2024
|
||||
*/
|
||||
@Named
|
||||
@ApplicationScoped
|
||||
public class AppBean {
|
||||
|
||||
public AppBean() {
|
||||
// TODO Auto-generated constructor stub
|
||||
}
|
||||
|
||||
private List<SelectItem> searchTypeList;
|
||||
|
||||
/**
|
||||
* @return the searchTypeList
|
||||
* @see RecipeSearchType
|
||||
* Used by main.xhtml
|
||||
*/
|
||||
public List<SelectItem> getSearchTypeList() {
|
||||
if (searchTypeList == null) {
|
||||
searchTypeList = loadSearchTypeList();
|
||||
}
|
||||
return searchTypeList;
|
||||
}
|
||||
|
||||
private List<SelectItem> loadSearchTypeList() {
|
||||
List<SelectItem> list = new ArrayList<SelectItem>(5);
|
||||
list.add(new SelectItem(RecipeSearchType.rst_BY_NAME.ordinal(),
|
||||
"Title"));
|
||||
list.add(new SelectItem(RecipeSearchType.rst_BY_CATEGORY.ordinal(),
|
||||
"Category"));
|
||||
list.add(new SelectItem(RecipeSearchType.rst_BY_CUISINE.ordinal(),
|
||||
"Cuisine"));
|
||||
list.add(
|
||||
new SelectItem(RecipeSearchType.rst_BY_INGREDIENT.ordinal(),
|
||||
"Ingredient"));
|
||||
return list;
|
||||
}
|
||||
}
|
127
src/main/java/com/mousetech/gourmetj/CookieBean.java
Normal file
127
src/main/java/com/mousetech/gourmetj/CookieBean.java
Normal file
|
@ -0,0 +1,127 @@
|
|||
/**
|
||||
* Copyright (C) 2024, Tim Holloway
|
||||
*
|
||||
* Manages app data persisted client-side in cookies.
|
||||
*
|
||||
* Date written: Jan 31, 2024
|
||||
* Author: Tim Holloway <timh@mousetech.com>
|
||||
*/
|
||||
package com.mousetech.gourmetj;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import jakarta.faces.view.ViewScoped;
|
||||
import jakarta.inject.Named;
|
||||
|
||||
/**
|
||||
* Caching object for cookie data persistence.
|
||||
*
|
||||
* @author timh
|
||||
* @since Jan 31, 2024
|
||||
*/
|
||||
@Named
|
||||
@ViewScoped
|
||||
public class CookieBean {
|
||||
|
||||
private static final String KEY_DISPLAY_ROWS = "displayRows";
|
||||
|
||||
private static final String KEY_SEARCH_TYPE = "searchType";
|
||||
|
||||
private static final String KEY_SEARCH_FOR = "searchFor";
|
||||
|
||||
/* Logger */
|
||||
|
||||
private static final Logger log =
|
||||
LoggerFactory.getLogger(CookieBean.class);
|
||||
|
||||
private Map<String, String> cookieMap;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public CookieBean() {
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
this.cookieMap = JSFUtils.getCookies();
|
||||
}
|
||||
|
||||
/**
|
||||
* Persist us to client cookie storage
|
||||
*
|
||||
* @throws UnsupportedEncodingException (which should never
|
||||
* happen)
|
||||
*/
|
||||
public void saveCookies()
|
||||
throws UnsupportedEncodingException {
|
||||
final Map<String, Object> properties = new HashMap<>();
|
||||
properties.put("maxAge", 31536000);
|
||||
properties.put("path", "/");
|
||||
properties.put("SameSite", "Strict");
|
||||
|
||||
for (Entry<String, String> e : cookieMap.entrySet()) {
|
||||
JSFUtils.outputCookie(e.getKey(), e.getValue(),
|
||||
properties);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Cookie value by name
|
||||
*
|
||||
* @param name Name of the cookie
|
||||
* @return Value stored in the cookie
|
||||
*/
|
||||
public String getCookieValue(String name) {
|
||||
return cookieMap.get(name);
|
||||
}
|
||||
|
||||
public void setCookieValue(String name, String value) {
|
||||
cookieMap.put(name, value);
|
||||
}
|
||||
|
||||
// ************************
|
||||
// App-specific properties
|
||||
// ************************
|
||||
|
||||
public String getSearchText() {
|
||||
return cookieMap.get(KEY_SEARCH_FOR);
|
||||
}
|
||||
|
||||
public void setSearchText(String value) {
|
||||
cookieMap.put(KEY_SEARCH_FOR, value);
|
||||
}
|
||||
|
||||
// **
|
||||
public Integer getSearchType() {
|
||||
if (!cookieMap.containsKey(KEY_SEARCH_TYPE)) {
|
||||
cookieMap.put(KEY_SEARCH_TYPE, "0");
|
||||
}
|
||||
String st = cookieMap.get(KEY_SEARCH_TYPE);
|
||||
return Integer.valueOf(String.valueOf(st));
|
||||
}
|
||||
|
||||
public void setSearchType(Integer value) {
|
||||
cookieMap.put(KEY_SEARCH_TYPE, String.valueOf(value));
|
||||
}
|
||||
|
||||
// **
|
||||
public Integer getDisplayListSize() {
|
||||
if (!cookieMap.containsKey(KEY_DISPLAY_ROWS)) {
|
||||
cookieMap.put(KEY_DISPLAY_ROWS, "30");
|
||||
}
|
||||
String st = cookieMap.get(KEY_DISPLAY_ROWS);
|
||||
return Integer.valueOf(String.valueOf(st));
|
||||
}
|
||||
|
||||
public void setDisplayListSize(Integer value) {
|
||||
cookieMap.put(KEY_DISPLAY_ROWS, String.valueOf(value));
|
||||
}
|
||||
}
|
|
@ -1,11 +1,17 @@
|
|||
package com.mousetech.gourmetj;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import jakarta.faces.application.FacesMessage;
|
||||
import jakarta.faces.context.ExternalContext;
|
||||
import jakarta.faces.context.FacesContext;
|
||||
import jakarta.faces.context.Flash;
|
||||
import jakarta.servlet.http.Cookie;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -55,6 +61,14 @@ public class JSFUtils {
|
|||
message);
|
||||
}
|
||||
|
||||
|
||||
public static void addWarningMessage(String msgString) {
|
||||
FacesMessage message = new FacesMessage(
|
||||
FacesMessage.SEVERITY_WARN, "WARNING", msgString);
|
||||
FacesContext.getCurrentInstance().addMessage(null,
|
||||
message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Post an error-level message to the FacesContext where the
|
||||
* <h:messages> tag can display it.
|
||||
|
@ -103,4 +117,36 @@ public class JSFUtils {
|
|||
public static void putFlash(String key, Object value) {
|
||||
flashScope().put(key, value);
|
||||
}
|
||||
|
||||
//***********
|
||||
//* COOKIE!!!
|
||||
//***********
|
||||
|
||||
/**
|
||||
* Get cookie values.
|
||||
*/
|
||||
public static Map<String, String> getCookies(){
|
||||
Map<String, Object> m0 = getExternalContext().getRequestCookieMap();
|
||||
Map<String, String>m1 = new HashMap<String, String>();
|
||||
m1 = m0.entrySet()
|
||||
.stream()
|
||||
.collect(Collectors.toMap(
|
||||
e -> e.getKey(),
|
||||
e -> ((Cookie)e.getValue()).getValue()));
|
||||
return m1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a cookie value in Response.
|
||||
* @param name Cookie name
|
||||
* @param value Cookie value
|
||||
* @param properties Cookie property Map (timeout, <i>etc.</i>)
|
||||
* @throws UnsupportedEncodingException
|
||||
*/
|
||||
public static void outputCookie(String name,
|
||||
String value, Map<String, Object> properties) throws UnsupportedEncodingException {
|
||||
getExternalContext().addResponseCookie(name,
|
||||
URLEncoder.encode(value, "UTF-8"),
|
||||
properties);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -129,32 +129,6 @@ public class UserSession implements Serializable {
|
|||
this.searchType = searchType;
|
||||
}
|
||||
|
||||
private List<SelectItem> searchTypeList;
|
||||
|
||||
/**
|
||||
* @return the searchTypeList
|
||||
*/
|
||||
public List<SelectItem> getSearchTypeList() {
|
||||
if (searchTypeList == null) {
|
||||
searchTypeList = loadSearchTypeList();
|
||||
}
|
||||
return searchTypeList;
|
||||
}
|
||||
|
||||
private List<SelectItem> loadSearchTypeList() {
|
||||
List<SelectItem> list = new ArrayList<SelectItem>(5);
|
||||
list.add(new SelectItem(RecipeSearchType.rst_BY_NAME,
|
||||
"Title"));
|
||||
list.add(new SelectItem(RecipeSearchType.rst_BY_CATEGORY,
|
||||
"Category"));
|
||||
list.add(new SelectItem(RecipeSearchType.rst_BY_CUISINE,
|
||||
"Cuisine"));
|
||||
list.add(
|
||||
new SelectItem(RecipeSearchType.rst_BY_INGREDIENT,
|
||||
"Ingredient"));
|
||||
return list;
|
||||
}
|
||||
|
||||
// ====
|
||||
|
||||
public String formatCategories(Recipe r) {
|
||||
|
@ -190,16 +164,17 @@ public class UserSession implements Serializable {
|
|||
private List<Recipe> shoppingList = new ArrayList<Recipe>();
|
||||
|
||||
/**
|
||||
* @return the sessionTimeoutInterval
|
||||
* @return the sessionTimeoutInterval, msec
|
||||
*/
|
||||
public long getSessionTimeoutInterval() {
|
||||
return sessionTimeoutInterval;
|
||||
return 5000L; //sessionTimeoutInterval;
|
||||
}
|
||||
|
||||
public void sessionIdleListener() {
|
||||
log.warn("Session Idle Listener fired.");
|
||||
PrimeFaces.current()
|
||||
.executeScript("sessionExpiredConfirmation.show()");
|
||||
JSFUtils.addWarningMessage("Timeout approaching. Save your work!");
|
||||
// PrimeFaces.current()
|
||||
// .executeScript("sessionExpiredConfirmation.show()");
|
||||
}
|
||||
|
||||
public String logoutAction() {
|
||||
|
|
|
@ -6,10 +6,9 @@
|
|||
xmlns:ui="http://java.sun.com/jsf/facelets"
|
||||
xmlns:p="http://primefaces.org/ui"
|
||||
>
|
||||
<head></head>
|
||||
<body>
|
||||
<h:head></h:head>
|
||||
<h: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"
|
||||
|
@ -29,50 +28,14 @@
|
|||
</ui:insert>
|
||||
<!-- -->
|
||||
<div id="footer">
|
||||
(C) 2021 Tim Holloway, Licensed under the <a
|
||||
(C) 2021, 2024 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>
|
||||
</div>
|
||||
<!-- -->
|
||||
<h:form id="frmTimeout">
|
||||
<p:idleMonitor
|
||||
timeout="#{userSession.sessionTimeoutInterval}"
|
||||
onidle="PF('dlgSessionExpired').show()"
|
||||
>
|
||||
<p:ajax event="idle"
|
||||
listener="#{userSession.sessionIdleListener}"
|
||||
/>
|
||||
</p:idleMonitor>
|
||||
<p:confirmDialog closable="false"
|
||||
id="sessionExpiredDlg"
|
||||
message="Your session has expired."
|
||||
header="#{msgs['confirmDialog.initiatingDestroyProcess.label']}"
|
||||
severity="alert"
|
||||
widgetVar="dlgSessionExpired"
|
||||
style="z-index: 25000"
|
||||
>
|
||||
<p:commandButton id="cmdExpiredOK"
|
||||
value="OK" action="/main.jsf"
|
||||
oncomplete="PF('dlgSessionExpired').hide()"
|
||||
/>
|
||||
</p:confirmDialog>
|
||||
</h:form>
|
||||
<!-- -->
|
||||
<h:form id="frmOpErr">
|
||||
<p:confirmDialog
|
||||
message="Session may have expired."
|
||||
header="Error"
|
||||
severity="alert" widgetVar="opError"
|
||||
>
|
||||
<p:commandButton value="OK"
|
||||
oncomplete="PF('opError').hide()"
|
||||
/>
|
||||
</p:confirmDialog>
|
||||
</h:form>
|
||||
</h:body>
|
||||
</f:view>
|
||||
</ui:composition>
|
||||
</body>
|
||||
</h:body>
|
||||
</html>
|
|
@ -395,6 +395,19 @@
|
|||
/>
|
||||
</h:form>
|
||||
</p:panel>
|
||||
<!-- -->
|
||||
<p:growl id="growl" showDetail="true" />
|
||||
<h:form id="frmTimeout">
|
||||
<p:idleMonitor
|
||||
timeout="#{userSession.sessionTimeoutInterval}"
|
||||
>
|
||||
<p:ajax id="ajaxIdle" event="idle"
|
||||
listener="#{userSession.sessionIdleListener}"
|
||||
update="growl"
|
||||
/>
|
||||
</p:idleMonitor>
|
||||
</h:form>
|
||||
<!-- -->
|
||||
<p:dialog id="addGroupDlg" widgetVar="addGroupDlg">
|
||||
<h:form id="frmAddGroup">
|
||||
<p:panelGrid columns="1">
|
||||
|
|
|
@ -28,10 +28,10 @@
|
|||
/>
|
||||
<p:outputLabel for="@next" value="Search for " />
|
||||
<p:selectOneMenu id="ctlSearchType"
|
||||
value="#{userSession.searchType}"
|
||||
value="#{cookieBean.searchType}"
|
||||
>
|
||||
<f:selectItems
|
||||
value="#{userSession.searchTypeList}"
|
||||
value="#{appBean.searchTypeList}"
|
||||
/>
|
||||
<p:ajax
|
||||
listener="#{adminMainBean.resetSuggestions}"
|
||||
|
@ -45,12 +45,12 @@
|
|||
<p:commandButton value="New Recipe"
|
||||
action="#{adminMainBean.doNewRecipe}"
|
||||
/>
|
||||
<p:commandButton value="More..."
|
||||
<p:commandButton value="Shopping..."
|
||||
action="#{adminMainBean.doMore}"
|
||||
/>
|
||||
<h:outputText id="slistSSize"
|
||||
style="margin-left: 2em"
|
||||
value="#{userSession.shoppingList.size()}"
|
||||
value="#{cookieBean.displayListSize}"
|
||||
/>
|
||||
<h:outputLabel for="slistSize"
|
||||
value=" Recipes in Shopping List"
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
xmlns:p="http://primefaces.org/ui"
|
||||
xmlns:c="http://xmlns.jcp.org/jstl"
|
||||
>
|
||||
<!-- Tabbed page for the Mainpage "More..." button -->
|
||||
<!-- Tabbed page for the Mainpage "Shopping..." button -->
|
||||
<ui:define name="title">Gourmet Recipe Manager - Shopping</ui:define>
|
||||
<ui:define name="content">
|
||||
<style>
|
||||
|
@ -34,6 +34,7 @@
|
|||
|
||||
}
|
||||
</style>
|
||||
This is the list of recipe items you've selected to shop for.
|
||||
<h:messages />
|
||||
<p:tabView id="tabGroupClient" orientation="left"
|
||||
dynamic="true"
|
||||
|
@ -95,13 +96,11 @@
|
|||
value="Ingredients"
|
||||
/>
|
||||
</f:facet>
|
||||
<p:headerRow>
|
||||
<p:column colspan="4">
|
||||
<h:outputText
|
||||
value="#{item.shopCat}"
|
||||
/>
|
||||
</p:column>
|
||||
</p:headerRow>
|
||||
<p:column label="Amt"
|
||||
style="width: 3em; text-align: right"
|
||||
>
|
||||
|
@ -150,6 +149,7 @@
|
|||
</p:tab>
|
||||
<!-- -->
|
||||
<p:tab id="tabPantry" title="Pantry">
|
||||
<h:outputText value="Stuff already in the pantry." />
|
||||
<h:outputText value="For future implementation" />
|
||||
</p:tab>
|
||||
<!-- -->
|
||||
|
|
|
@ -26,6 +26,11 @@ server:
|
|||
servlet:
|
||||
session:
|
||||
timeout: '30m'
|
||||
# Theme here pverrides jinfaces theme
|
||||
# context-parameters:
|
||||
# primefaces:
|
||||
# THEME: vela
|
||||
|
||||
|
||||
gourmet:
|
||||
password:
|
||||
|
@ -33,4 +38,4 @@ gourmet:
|
|||
|
||||
joinfaces:
|
||||
primefaces:
|
||||
theme: saga
|
||||
theme: casablanca
|
||||
|
|
Loading…
Reference in New Issue
Block a user