Merge pull request 'FEAT: Implemented Recipe of the Week' (#35) from dev into master
All checks were successful
Deploy application with Docker / build_and_deploy (push) Successful in 44s
All checks were successful
Deploy application with Docker / build_and_deploy (push) Successful in 44s
Reviewed-on: #35
This commit is contained in:
commit
fd0fb2d98a
@ -347,6 +347,12 @@ found in **OTHER** section.
|
||||
- [ ] Read (Required, Default: F) boolean
|
||||
- [ ] Created (Required) date/time stamp
|
||||
|
||||
- [x] RecipeOfTheWeek: Represents the recipe of the week.
|
||||
- [x] ID (PK) Serial
|
||||
- [x] RecipeId (FK: Recipe.Id, Required) Serial
|
||||
- [x] Score (Required) float (Computed score)
|
||||
- [x] Created (Required) date/time stamp (serves as the validity)
|
||||
|
||||
|
||||
'**': Not sure implementation
|
||||
|
||||
|
||||
@ -48,9 +48,29 @@ func HomePage(ctx *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
page = templates.HomePage(true, viewedRecipes, madeRecipes)
|
||||
// Get the recipe of the week
|
||||
recipeOfTheWeek, err := deps.RecipeService.GetRecipeOfTheWeek(&userId)
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, gin.H{
|
||||
"status": http.StatusInternalServerError,
|
||||
"message": fmt.Sprintf("Error getting made recipes. %s\n", err.Error()),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
page = templates.HomePage(true, viewedRecipes, madeRecipes, recipeOfTheWeek)
|
||||
} else {
|
||||
page = templates.HomePage(false, nil, nil)
|
||||
// Get the recipe of the week
|
||||
recipeOfTheWeek, err := deps.RecipeService.GetRecipeOfTheWeek(nil)
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, gin.H{
|
||||
"status": http.StatusInternalServerError,
|
||||
"message": fmt.Sprintf("Error getting made recipes. %s\n", err.Error()),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
page = templates.HomePage(false, nil, nil, recipeOfTheWeek)
|
||||
}
|
||||
|
||||
title := "Potion - Home"
|
||||
|
||||
@ -174,6 +174,9 @@ func (s *RecipeService) GetUserFavoriteRecipes(id int) ([]domain.Recipe, error)
|
||||
return s.recipeRepository.GetUserFavoriteRecipes(id)
|
||||
}
|
||||
|
||||
// GetUserViewedRecipes returns a list of the most recent x (limit) recipes viewed by a user, from
|
||||
// the provided userId. This will return a list of size 'limit'. Any errors will be bubbled up to
|
||||
// the caller.
|
||||
func (s *RecipeService) GetUserViewedRecipes(userId, limit int) ([]domain.Recipe, error) {
|
||||
engagement, err := s.engagementRepository.GetUserEngagementFiltered(userId, limit, domainEngagement.EngagementViewed)
|
||||
if err != nil {
|
||||
@ -188,6 +191,9 @@ func (s *RecipeService) GetUserViewedRecipes(userId, limit int) ([]domain.Recipe
|
||||
return s.recipeRepository.GetRecipes(ids, &userId)
|
||||
}
|
||||
|
||||
// GetUserMadeRecipes returns a list of the most recent x (limit) recipes made by a user, from the
|
||||
// provided userId. This will return a list of size 'limit'. Any errors will be bubbled up to the
|
||||
// caller.
|
||||
func (s *RecipeService) GetUserMadeRecipes(userId, limit int) ([]domain.Recipe, error) {
|
||||
engagement, err := s.engagementRepository.GetUserEngagementFiltered(userId, limit, domainEngagement.EngagementMade)
|
||||
if err != nil {
|
||||
@ -201,3 +207,9 @@ func (s *RecipeService) GetUserMadeRecipes(userId, limit int) ([]domain.Recipe,
|
||||
|
||||
return s.recipeRepository.GetRecipes(ids, &userId)
|
||||
}
|
||||
|
||||
// GetRecipeOfTheWeek searches for the most recent recipe of the week. If there is not a value,
|
||||
// the recipe will be nil. Any errors will be bubbled to the caller.
|
||||
func (s *RecipeService) GetRecipeOfTheWeek(userId *int) (*domain.Recipe, error) {
|
||||
return s.recipeRepository.GetRecipeOfTheWeek(userId)
|
||||
}
|
||||
|
||||
@ -10,4 +10,5 @@ type RecipeRepository interface {
|
||||
GetUserFavoriteRecipes(id int) ([]Recipe, error)
|
||||
GetRecipeTags(recipe *Recipe) error
|
||||
GetRecipeFavorite(recipe *Recipe, userId int) error
|
||||
GetRecipeOfTheWeek(userId *int) (*Recipe, error)
|
||||
}
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
package domain
|
||||
|
||||
import "github.com/gin-gonic/gin"
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type RecipeService interface {
|
||||
CreateRecipe(ctx *gin.Context) (*Recipe, error)
|
||||
@ -10,4 +12,5 @@ type RecipeService interface {
|
||||
GetUserFavoriteRecipes(id int) ([]Recipe, error)
|
||||
GetUserViewedRecipes(userId, limit int) ([]Recipe, error)
|
||||
GetUserMadeRecipes(userId, limit int) ([]Recipe, error)
|
||||
GetRecipeOfTheWeek(userId *int) (*Recipe, error)
|
||||
}
|
||||
|
||||
@ -0,0 +1,14 @@
|
||||
-- Author: Hayden Hargreaves (hhargreaves2006@gmail.com)
|
||||
-- Desc: Create the recipe of the week table.
|
||||
-- Date: 07/26/2025
|
||||
|
||||
BEGIN;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS RecipeOfTheWeek (
|
||||
Id SERIAL PRIMARY KEY NOT NULL,
|
||||
RecipeId INTEGER NOT NULL REFERENCES recipes(id),
|
||||
Score NUMERIC(10, 4) NOT NULL,
|
||||
Created TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
COMMIT;
|
||||
@ -0,0 +1,41 @@
|
||||
-- Author: Hayden Hargreaves (hhargreaves2006@gmail.com)
|
||||
-- Desc: Create the recipe of the week stored procedure.
|
||||
-- Date: 07/26/2025
|
||||
|
||||
CREATE OR REPLACE PROCEDURE calculate_recipe_of_the_week_procedure()
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
BEGIN
|
||||
-- Insert the highest-scoring recipe from the last 7 days into daily_top_recipes
|
||||
INSERT INTO RecipeOfTheWeek (RecipeId, Score, Created)
|
||||
SELECT
|
||||
e.Entity AS RecipeId,
|
||||
(
|
||||
-- Weights can be configured here
|
||||
SUM(CASE WHEN e.Type = 'viewed' THEN 1 ELSE 0 END) * 0.20 +
|
||||
SUM(CASE WHEN e.Type = 'made' THEN 1 ELSE 0 END) * 0.40 +
|
||||
SUM(CASE WHEN e.Type = 'liked' THEN 1 ELSE 0 END) * 0.30 +
|
||||
SUM(CASE WHEN e.Type = 'shared' THEN 1 ELSE 0 END) * 0.10
|
||||
) AS Score,
|
||||
NOW()
|
||||
FROM
|
||||
Engagements e
|
||||
WHERE
|
||||
e.Created >= NOW() - INTERVAL '7 days'
|
||||
AND e.Entity IS NOT NULL
|
||||
GROUP BY e.Entity
|
||||
ORDER BY Score DESC
|
||||
LIMIT 1;
|
||||
|
||||
RAISE NOTICE 'Successfully calculated and stored the top recipe for the day.';
|
||||
|
||||
|
||||
EXCEPTION
|
||||
WHEN NO_DATA_FOUND THEN
|
||||
-- Handle cases where no engagements are found in the last 7 days
|
||||
RAISE NOTICE 'No engagement data found for the last 7 days to calculate a top recipe.';
|
||||
WHEN OTHERS THEN
|
||||
-- Catch any other potential errors and re-raise them after logging
|
||||
RAISE EXCEPTION 'An error occurred during top recipe calculation: %', SQLERRM;
|
||||
END;
|
||||
$$;
|
||||
@ -676,6 +676,9 @@ func (r *RecipeRepository) GetUserRecipes(id int) ([]domain.Recipe, error) {
|
||||
return recipes, nil
|
||||
}
|
||||
|
||||
// GetUserRecipes gets a list of a users favorited recipes. This function does not ensure the user is
|
||||
// authenticated or exists. If nothing is found, a blank slice will be returned. The resulting list
|
||||
// is sorted by the created dates, newest first. Any errors will be bubbled to the caller.
|
||||
func (r *RecipeRepository) GetUserFavoriteRecipes(id int) ([]domain.Recipe, error) {
|
||||
tx, err := r.db.Begin()
|
||||
if err != nil {
|
||||
@ -766,8 +769,13 @@ func (r *RecipeRepository) GetUserFavoriteRecipes(id int) ([]domain.Recipe, erro
|
||||
|
||||
// GetRecipeTags requires a recipe to be filled with at least an ID. This function will use the ID
|
||||
// defined in the provided recipe to fill the Tags array with the recipe's tags from the database.
|
||||
// The recipe is modified in place and is not returned. Any errors will be bubbled to the caller.
|
||||
// The recipe is modified in place and is not returned. If the recipe is nil, the function will
|
||||
// return nothing (skipping). Any errors will be bubbled to the caller.
|
||||
func (r *RecipeRepository) GetRecipeTags(recipe *domain.Recipe) error {
|
||||
if recipe == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
tx, err := r.db.Begin()
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
@ -808,8 +816,13 @@ func (r *RecipeRepository) GetRecipeTags(recipe *domain.Recipe) error {
|
||||
|
||||
// GetRecipeFavorite requires a recipe to be filled with at least an ID. This function will use the
|
||||
// ID defined in the provided recipe to fill the favorite status of the recipe, based on the provided
|
||||
// userId. The recipe is modified in place and is not returned. Any errors will be bubbled to the caller.
|
||||
// userId. The recipe is modified in place and is not returned. If the recipe is nil, the function
|
||||
// will return nothing (skipping). Any errors will be bubbled to the caller.
|
||||
func (r *RecipeRepository) GetRecipeFavorite(recipe *domain.Recipe, userId int) error {
|
||||
if recipe == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
tx, err := r.db.Begin()
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
@ -832,3 +845,90 @@ func (r *RecipeRepository) GetRecipeFavorite(recipe *domain.Recipe, userId int)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetRecipeOfTheWeek searches for the most recent recipe of the week. If there is not a value,
|
||||
// the recipe will be nil. This function simply collects the most recent entry in the recipeoftheweek
|
||||
// table and return it. Any errors will be bubbled to the caller.
|
||||
func (r *RecipeRepository) GetRecipeOfTheWeek(userId *int) (*domain.Recipe, error) {
|
||||
tx, err := r.db.Begin()
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
query := `
|
||||
SELECT
|
||||
r.id, r.title, r.description, r.instructions, r.serves, r.difficulty, r.duration, r.category,
|
||||
r.ingredients, r.userid, r.modified, r.created
|
||||
FROM recipes r
|
||||
JOIN recipeoftheweek rw ON rw.recipeid = r.id
|
||||
ORDER BY created DESC
|
||||
LIMIT 1;
|
||||
`
|
||||
|
||||
var durationBytes []byte
|
||||
var ingredientBytes []byte
|
||||
|
||||
var recipe domain.Recipe
|
||||
if err := tx.QueryRow(query).Scan(
|
||||
&recipe.Id,
|
||||
&recipe.Title,
|
||||
&recipe.Description,
|
||||
pq.Array(&recipe.Instructions),
|
||||
&recipe.Serves,
|
||||
&recipe.Difficulty,
|
||||
&durationBytes,
|
||||
&recipe.Category,
|
||||
&ingredientBytes,
|
||||
&recipe.UserId,
|
||||
&recipe.Modified,
|
||||
&recipe.Created,
|
||||
); err != nil {
|
||||
return nil, fmt.Errorf("Failed to location recipe in database: %s", err.Error())
|
||||
}
|
||||
|
||||
// Parse duration
|
||||
if len(durationBytes) > 0 {
|
||||
var duration domain.RecipeDuration
|
||||
if err := json.Unmarshal(durationBytes, &duration); err != nil {
|
||||
return nil, fmt.Errorf("Failed to parse duration from database: %s", err.Error())
|
||||
}
|
||||
|
||||
recipe.Duration = duration
|
||||
} else {
|
||||
recipe.Duration = domain.RecipeDuration{}
|
||||
}
|
||||
|
||||
// Parse ingredient
|
||||
if len(ingredientBytes) > 0 {
|
||||
var ingredients []domain.RecipeIngredient
|
||||
if err := json.Unmarshal(ingredientBytes, &ingredients); err != nil {
|
||||
return nil, fmt.Errorf("Failed to parse ingredients from database: %s", err.Error())
|
||||
}
|
||||
|
||||
recipe.Ingredients = ingredients
|
||||
} else {
|
||||
recipe.Ingredients = []domain.RecipeIngredient{}
|
||||
}
|
||||
|
||||
// Add tags
|
||||
if err := r.GetRecipeTags(&recipe); err != nil {
|
||||
fmt.Printf("ERROR getting recipe tags. %s\n", err.Error())
|
||||
}
|
||||
|
||||
// Get favorite status, if user id is provided
|
||||
if userId != nil {
|
||||
if err := r.GetRecipeFavorite(&recipe, *userId); err != nil {
|
||||
fmt.Printf("ERROR getting recipe favorite status. %s\n", err.Error())
|
||||
}
|
||||
} else {
|
||||
recipe.Favorite = false
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
tx.Rollback()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &recipe, nil
|
||||
}
|
||||
|
||||
@ -5,20 +5,18 @@ import "github.com/haydenhargreaves/Potion/internal/domain/recipe"
|
||||
import domainServer "github.com/haydenhargreaves/Potion/internal/domain/server"
|
||||
|
||||
templ likeButton() {
|
||||
<button class="hover:cursor-pointer">
|
||||
<svg class="h-6 text-red-500" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M2 9.1371C2 14 6.01943 16.5914 8.96173 18.9109C10 19.7294 11 20.5 12 20.5C13 20.5 14 19.7294 15.0383 18.9109C17.9806 16.5914 22 14 22 9.1371C22 4.27416 16.4998 0.825464 12 5.50063C7.50016 0.825464 2 4.27416 2 9.1371Z"
|
||||
fill="currentColor"
|
||||
></path>
|
||||
</svg>
|
||||
</button>
|
||||
<svg class="h-6 text-red-500" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M2 9.1371C2 14 6.01943 16.5914 8.96173 18.9109C10 19.7294 11 20.5 12 20.5C13 20.5 14 19.7294 15.0383 18.9109C17.9806 16.5914 22 14 22 9.1371C22 4.27416 16.4998 0.825464 12 5.50063C7.50016 0.825464 2 4.27416 2 9.1371Z"
|
||||
fill="currentColor"
|
||||
></path>
|
||||
</svg>
|
||||
}
|
||||
|
||||
templ RecipeCardSmall(recipe domain.Recipe) {
|
||||
<div class="flex flex-col items-center justify-between rounded-lg border-gray-300 border shadow-md p-4 flex-shrink-0">
|
||||
<img class="size-52 md:size-48 rounded-sm" src=""/>
|
||||
<div class="w-full mt-8">
|
||||
<img class="size-52 md:size-48 rounded-sm" src="/v1/web/static/img/recipe_placeholder.png" type="image/png"/>
|
||||
<div class="w-52 md:w-48 mt-8">
|
||||
<h2 class="font-semibold overflow-hidden whitespace-nowrap text-ellipsis">
|
||||
{ recipe.Title }
|
||||
</h2>
|
||||
@ -55,36 +53,39 @@ templ ContentCardSmall(content, target string) {
|
||||
</div>
|
||||
}
|
||||
|
||||
// TODO: Implement this using a recipe type parameter!
|
||||
templ RecipeCardLarge(liked bool) {
|
||||
<div class="w-9/10 md:w-2/5 flex flex-col items-center justify-between rounded-lg border-gray-300 border shadow-md p-4">
|
||||
<img class="size-80 md:size-64 rounded-sm" src=""/>
|
||||
<div class="mt-8">
|
||||
<h2 class="font-semibold">Avocado Toast</h2>
|
||||
<p class="text-xs overflow-hidden whitespace-nowrap text-ellipsis">
|
||||
Hayden Hargreaves
|
||||
</p>
|
||||
<p class="text-sm overflow-hidden [display:-webkit-box] [-webkit-box-orient:vertical] [-webkit-line-clamp:4] my-2">
|
||||
Avocado toast is a delicious and simple breakfast, snack or light meal! Learn how to
|
||||
make the BEST avocado toast with this recipe, plus fun variations.
|
||||
Avocado toast is a delicious and simple breakfast, snack or light meal! Learn how to
|
||||
make the BEST avocado toast with this recipe, plus fun variations.
|
||||
Avocado toast is a delicious and simple breakfast, snack or light meal! Learn how to
|
||||
make the BEST avocado toast with this recipe, plus fun variations.
|
||||
</p>
|
||||
<div class="flex items-center justify-between">
|
||||
<p class="text-xs my-2 bg-gray-200 rounded-lg w-fit px-2 py-1 mt-2">
|
||||
Breakfast - 15 min
|
||||
templ RecipeCardLarge(recipe *domain.Recipe) {
|
||||
if recipe != nil {
|
||||
<div class="flex flex-col items-center justify-between rounded-lg border-gray-300 border shadow-md p-4 flex-shrink-0">
|
||||
<img class="size-80 rounded-sm" src="/v1/web/static/img/recipe_placeholder.png" type="image/png"/>
|
||||
<div class="w-full mt-8">
|
||||
<h2 class="font-semibold overflow-hidden whitespace-nowrap text-ellipsis">
|
||||
{ recipe.Title }
|
||||
</h2>
|
||||
<p class="text-xs overflow-hidden whitespace-nowrap text-ellipsis">
|
||||
Serves { recipe.Serves }
|
||||
</p>
|
||||
if liked {
|
||||
@likeButton()
|
||||
}
|
||||
<p class="text-sm text-wrap w-80">
|
||||
{ recipe.Description }
|
||||
</p>
|
||||
<div class="flex items-end justify-between">
|
||||
<p class="text-xs mt-4 bg-gray-200 rounded-lg w-fit px-2 py-1">
|
||||
{ recipe.Category } - { recipe.Duration.Total } mins
|
||||
</p>
|
||||
if recipe.Favorite {
|
||||
@likeButton()
|
||||
}
|
||||
</div>
|
||||
<button
|
||||
hx-post={ fmt.Sprintf(domainServer.API_ENGAGEMENT_VIEW, recipe.Id) }
|
||||
hx-trigger="click"
|
||||
hx-swap="none"
|
||||
class="w-full rounded-lg py-2 bg-gradient-to-r from-blue-400 to-blue-600 text-white mt-2 hover:ring-blue-700 hover:shadow shadow-blue-300 duration-200 cursor-pointer"
|
||||
>
|
||||
Make Now!
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
class="w-full rounded-lg py-2 bg-gradient-to-r from-blue-400 to-blue-600 text-white mt-2 hover:ring-blue-700 hover:shadow shadow-blue-300 duration-200"
|
||||
>
|
||||
Make Now!
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
} else {
|
||||
<h2 class="text-2xl md:text-3xl text-gray-400">Coming soon!</h2>
|
||||
}
|
||||
}
|
||||
|
||||
@ -33,7 +33,7 @@ func likeButton() templ.Component {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<button class=\"hover:cursor-pointer\"><svg class=\"h-6 text-red-500\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M2 9.1371C2 14 6.01943 16.5914 8.96173 18.9109C10 19.7294 11 20.5 12 20.5C13 20.5 14 19.7294 15.0383 18.9109C17.9806 16.5914 22 14 22 9.1371C22 4.27416 16.4998 0.825464 12 5.50063C7.50016 0.825464 2 4.27416 2 9.1371Z\" fill=\"currentColor\"></path></svg></button>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<svg class=\"h-6 text-red-500\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M2 9.1371C2 14 6.01943 16.5914 8.96173 18.9109C10 19.7294 11 20.5 12 20.5C13 20.5 14 19.7294 15.0383 18.9109C17.9806 16.5914 22 14 22 9.1371C22 4.27416 16.4998 0.825464 12 5.50063C7.50016 0.825464 2 4.27416 2 9.1371Z\" fill=\"currentColor\"></path></svg>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@ -62,14 +62,14 @@ func RecipeCardSmall(recipe domain.Recipe) templ.Component {
|
||||
templ_7745c5c3_Var2 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<div class=\"flex flex-col items-center justify-between rounded-lg border-gray-300 border shadow-md p-4 flex-shrink-0\"><img class=\"size-52 md:size-48 rounded-sm\" src=\"\"><div class=\"w-full mt-8\"><h2 class=\"font-semibold overflow-hidden whitespace-nowrap text-ellipsis\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<div class=\"flex flex-col items-center justify-between rounded-lg border-gray-300 border shadow-md p-4 flex-shrink-0\"><img class=\"size-52 md:size-48 rounded-sm\" src=\"/v1/web/static/img/recipe_placeholder.png\" type=\"image/png\"><div class=\"w-52 md:w-48 mt-8\"><h2 class=\"font-semibold overflow-hidden whitespace-nowrap text-ellipsis\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var3 string
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(recipe.Title)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/components/cards.templ`, Line: 23, Col: 18}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/components/cards.templ`, Line: 21, Col: 18}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@ -82,7 +82,7 @@ func RecipeCardSmall(recipe domain.Recipe) templ.Component {
|
||||
var templ_7745c5c3_Var4 string
|
||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(recipe.Serves)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/components/cards.templ`, Line: 26, Col: 26}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/components/cards.templ`, Line: 24, Col: 26}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@ -95,7 +95,7 @@ func RecipeCardSmall(recipe domain.Recipe) templ.Component {
|
||||
var templ_7745c5c3_Var5 string
|
||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(recipe.Category)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/components/cards.templ`, Line: 30, Col: 22}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/components/cards.templ`, Line: 28, Col: 22}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@ -108,7 +108,7 @@ func RecipeCardSmall(recipe domain.Recipe) templ.Component {
|
||||
var templ_7745c5c3_Var6 string
|
||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(recipe.Duration.Total)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/components/cards.templ`, Line: 30, Col: 50}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/components/cards.templ`, Line: 28, Col: 50}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@ -131,7 +131,7 @@ func RecipeCardSmall(recipe domain.Recipe) templ.Component {
|
||||
var templ_7745c5c3_Var7 string
|
||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf(domainServer.API_ENGAGEMENT_VIEW, recipe.Id))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/components/cards.templ`, Line: 37, Col: 70}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/components/cards.templ`, Line: 35, Col: 70}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@ -182,7 +182,7 @@ func ContentCardSmall(content, target string) templ.Component {
|
||||
var templ_7745c5c3_Var10 string
|
||||
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(content)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/components/cards.templ`, Line: 52, Col: 32}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/components/cards.templ`, Line: 50, Col: 32}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@ -196,8 +196,7 @@ func ContentCardSmall(content, target string) templ.Component {
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: Implement this using a recipe type parameter!
|
||||
func RecipeCardLarge(liked bool) templ.Component {
|
||||
func RecipeCardLarge(recipe *domain.Recipe) templ.Component {
|
||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||
@ -218,19 +217,104 @@ func RecipeCardLarge(liked bool) templ.Component {
|
||||
templ_7745c5c3_Var11 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "<div class=\"w-9/10 md:w-2/5 flex flex-col items-center justify-between rounded-lg border-gray-300 border shadow-md p-4\"><img class=\"size-80 md:size-64 rounded-sm\" src=\"\"><div class=\"mt-8\"><h2 class=\"font-semibold\">Avocado Toast</h2><p class=\"text-xs overflow-hidden whitespace-nowrap text-ellipsis\">Hayden Hargreaves</p><p class=\"text-sm overflow-hidden [display:-webkit-box] [-webkit-box-orient:vertical] [-webkit-line-clamp:4] my-2\">Avocado toast is a delicious and simple breakfast, snack or light meal! Learn how to make the BEST avocado toast with this recipe, plus fun variations. Avocado toast is a delicious and simple breakfast, snack or light meal! Learn how to make the BEST avocado toast with this recipe, plus fun variations. Avocado toast is a delicious and simple breakfast, snack or light meal! Learn how to make the BEST avocado toast with this recipe, plus fun variations.</p><div class=\"flex items-center justify-between\"><p class=\"text-xs my-2 bg-gray-200 rounded-lg w-fit px-2 py-1 mt-2\">Breakfast - 15 min</p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if liked {
|
||||
templ_7745c5c3_Err = likeButton().Render(ctx, templ_7745c5c3_Buffer)
|
||||
if recipe != nil {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "<div class=\"flex flex-col items-center justify-between rounded-lg border-gray-300 border shadow-md p-4 flex-shrink-0\"><img class=\"size-80 rounded-sm\" src=\"/v1/web/static/img/recipe_placeholder.png\" type=\"image/png\"><div class=\"w-full mt-8\"><h2 class=\"font-semibold overflow-hidden whitespace-nowrap text-ellipsis\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var12 string
|
||||
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(recipe.Title)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/components/cards.templ`, Line: 62, Col: 19}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "</h2><p class=\"text-xs overflow-hidden whitespace-nowrap text-ellipsis\">Serves ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var13 string
|
||||
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(recipe.Serves)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/components/cards.templ`, Line: 65, Col: 27}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "</p><p class=\"text-sm text-wrap w-80\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var14 string
|
||||
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(recipe.Description)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/components/cards.templ`, Line: 68, Col: 25}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "</p><div class=\"flex items-end justify-between\"><p class=\"text-xs mt-4 bg-gray-200 rounded-lg w-fit px-2 py-1\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var15 string
|
||||
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(recipe.Category)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/components/cards.templ`, Line: 72, Col: 23}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, " - ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var16 string
|
||||
templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(recipe.Duration.Total)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/components/cards.templ`, Line: 72, Col: 51}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, " mins</p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if recipe.Favorite {
|
||||
templ_7745c5c3_Err = likeButton().Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "</div><button hx-post=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var17 string
|
||||
templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf(domainServer.API_ENGAGEMENT_VIEW, recipe.Id))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/components/cards.templ`, Line: 79, Col: 71}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "\" hx-trigger=\"click\" hx-swap=\"none\" class=\"w-full rounded-lg py-2 bg-gradient-to-r from-blue-400 to-blue-600 text-white mt-2 hover:ring-blue-700 hover:shadow shadow-blue-300 duration-200 cursor-pointer\">Make Now!</button></div></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
} else {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "<h2 class=\"text-2xl md:text-3xl text-gray-400\">Coming soon!</h2>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "</div><button class=\"w-full rounded-lg py-2 bg-gradient-to-r from-blue-400 to-blue-600 text-white mt-2 hover:ring-blue-700 hover:shadow shadow-blue-300 duration-200\">Make Now!</button></div></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
@ -25,7 +25,7 @@ templ favoriteResult(recipe domain.Recipe) {
|
||||
hx-swap="none"
|
||||
class="w-full p-2 border-b border-gray-200 hover:bg-gray-100 duration-200 flex items-center flex-col md:flex-row even:bg-[#f8f8f8] cursor-pointer"
|
||||
>
|
||||
<img class="bg-gray-50 size-56 md:size-40 rounded-md border-0" src=""/>
|
||||
<img class="bg-gray-50 size-56 md:size-40 rounded-md border-0" src="/v1/web/static/img/recipe_placeholder.png" type="image/png" />
|
||||
<div class="text-gray-700 p-4 flex flex-col items-center md:items-start w-full">
|
||||
<div class="flex flex-col md:flex-row items-center md:items-start justify-between w-full">
|
||||
<div class="flex flex-col items-center md:items-start">
|
||||
|
||||
@ -97,7 +97,7 @@ func favoriteResult(recipe domain.Recipe) templ.Component {
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "\" hx-trigger=\"click\" hx-swap=\"none\" class=\"w-full p-2 border-b border-gray-200 hover:bg-gray-100 duration-200 flex items-center flex-col md:flex-row even:bg-[#f8f8f8] cursor-pointer\"><img class=\"bg-gray-50 size-56 md:size-40 rounded-md border-0\" src=\"\"><div class=\"text-gray-700 p-4 flex flex-col items-center md:items-start w-full\"><div class=\"flex flex-col md:flex-row items-center md:items-start justify-between w-full\"><div class=\"flex flex-col items-center md:items-start\"><h3 class=\"text-xl font-semibold text-black pb-1\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "\" hx-trigger=\"click\" hx-swap=\"none\" class=\"w-full p-2 border-b border-gray-200 hover:bg-gray-100 duration-200 flex items-center flex-col md:flex-row even:bg-[#f8f8f8] cursor-pointer\"><img class=\"bg-gray-50 size-56 md:size-40 rounded-md border-0\" src=\"/v1/web/static/img/recipe_placeholder.png\" type=\"image/png\"><div class=\"text-gray-700 p-4 flex flex-col items-center md:items-start w-full\"><div class=\"flex flex-col md:flex-row items-center md:items-start justify-between w-full\"><div class=\"flex flex-col items-center md:items-start\"><h3 class=\"text-xl font-semibold text-black pb-1\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
||||
@ -36,7 +36,7 @@ templ searchSection() {
|
||||
</section>
|
||||
}
|
||||
|
||||
templ highlightSection(liked bool) {
|
||||
templ highlightSection(recipeOfTheWeek *domainRecipe.Recipe) {
|
||||
<section class="w-full flex flex-col items-center justify-center my-8 py-4">
|
||||
@components.BannerText("Recipe of the Week!")
|
||||
<p class="leading-relaxed p-4 my-8">
|
||||
@ -47,7 +47,7 @@ templ highlightSection(liked bool) {
|
||||
resonate with our users!
|
||||
</p>
|
||||
<div class="flex items-center justify-center w-full">
|
||||
@components.RecipeCardLarge(false)
|
||||
@components.RecipeCardLarge(recipeOfTheWeek)
|
||||
</div>
|
||||
</section>
|
||||
}
|
||||
@ -59,10 +59,14 @@ templ listsSection(loggedIn bool, viewed, made []domainRecipe.Recipe) {
|
||||
<h3 class="text-lg mt-8 mx-4">Recently viewed</h3>
|
||||
if loggedIn {
|
||||
<div class="flex overflow-x-auto gap-x-4 mx-4 my-4">
|
||||
for _, recipe := range viewed {
|
||||
@components.RecipeCardSmall(recipe)
|
||||
if len(viewed) > 0 {
|
||||
for _, recipe := range viewed {
|
||||
@components.RecipeCardSmall(recipe)
|
||||
}
|
||||
@components.ContentCardSmall("View full history...", "/v1/web/history")
|
||||
} else {
|
||||
<p class="text-sm">You have not viewed any recipes. There is nothing to show.</p>
|
||||
}
|
||||
@components.ContentCardSmall("View full history...", "/v1/web/history")
|
||||
</div>
|
||||
} else {
|
||||
<div class="my-2 mx-4 text-gray-800">
|
||||
@ -74,10 +78,14 @@ templ listsSection(loggedIn bool, viewed, made []domainRecipe.Recipe) {
|
||||
<h3 class="text-lg mt-8 mx-4">Make again</h3>
|
||||
if loggedIn {
|
||||
<div class="flex overflow-x-auto gap-x-4 mx-4 my-4">
|
||||
for _, recipe := range made {
|
||||
@components.RecipeCardSmall(recipe)
|
||||
}
|
||||
@components.ContentCardSmall("View full history...", "/v1/web/history")
|
||||
if len(made) > 0 {
|
||||
for _, recipe := range made {
|
||||
@components.RecipeCardSmall(recipe)
|
||||
}
|
||||
@components.ContentCardSmall("View full history...", "/v1/web/history")
|
||||
} else {
|
||||
<p class="text-sm">You have not made any recipes. There is nothing to show.</p>
|
||||
}
|
||||
</div>
|
||||
} else {
|
||||
<div class="my-2 mx-4 text-gray-800">
|
||||
@ -114,13 +122,13 @@ templ ctaSection() {
|
||||
</section>
|
||||
}
|
||||
|
||||
templ HomePage(loggedIn bool, viewed, made []domainRecipe.Recipe) {
|
||||
templ HomePage(loggedIn bool, viewed, made []domainRecipe.Recipe, recipeOfTheWeek *domainRecipe.Recipe) {
|
||||
@components.Navbar("home")
|
||||
<div class="w-full h-fit flex justify-center">
|
||||
<div class="mx-2 md:mx-0 w-full md:w-1/2 md:pt-14 h-full border-l border-r border-gray-300 bg-white">
|
||||
@introSection()
|
||||
@searchSection()
|
||||
@highlightSection(false)
|
||||
@highlightSection(recipeOfTheWeek)
|
||||
@listsSection(loggedIn, viewed, made)
|
||||
@ctaSection()
|
||||
</div>
|
||||
|
||||
@ -86,7 +86,7 @@ func searchSection() templ.Component {
|
||||
})
|
||||
}
|
||||
|
||||
func highlightSection(liked bool) templ.Component {
|
||||
func highlightSection(recipeOfTheWeek *domainRecipe.Recipe) templ.Component {
|
||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||
@ -119,7 +119,7 @@ func highlightSection(liked bool) templ.Component {
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = components.RecipeCardLarge(false).Render(ctx, templ_7745c5c3_Buffer)
|
||||
templ_7745c5c3_Err = components.RecipeCardLarge(recipeOfTheWeek).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@ -169,22 +169,33 @@ func listsSection(loggedIn bool, viewed, made []domainRecipe.Recipe) templ.Compo
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
for _, recipe := range viewed {
|
||||
templ_7745c5c3_Err = components.RecipeCardSmall(recipe).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if len(viewed) > 0 {
|
||||
for _, recipe := range viewed {
|
||||
templ_7745c5c3_Err = components.RecipeCardSmall(recipe).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, " ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = components.ContentCardSmall("View full history...", "/v1/web/history").Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
} else {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "<p class=\"text-sm\">You have not viewed any recipes. There is nothing to show.</p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = components.ContentCardSmall("View full history...", "/v1/web/history").Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "</div>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "</div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
} else {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "<div class=\"my-2 mx-4 text-gray-800\"><a class=\"underline\" href=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "<div class=\"my-2 mx-4 text-gray-800\"><a class=\"underline\" href=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@ -193,36 +204,47 @@ func listsSection(loggedIn bool, viewed, made []domainRecipe.Recipe) templ.Compo
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "\"><p class=\"text-sm\">Log in to view metrics.</p></a></div>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "\"><p class=\"text-sm\">Log in to view metrics.</p></a></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "<h3 class=\"text-lg mt-8 mx-4\">Make again</h3>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "<h3 class=\"text-lg mt-8 mx-4\">Make again</h3>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if loggedIn {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "<div class=\"flex overflow-x-auto gap-x-4 mx-4 my-4\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "<div class=\"flex overflow-x-auto gap-x-4 mx-4 my-4\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
for _, recipe := range made {
|
||||
templ_7745c5c3_Err = components.RecipeCardSmall(recipe).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if len(made) > 0 {
|
||||
for _, recipe := range made {
|
||||
templ_7745c5c3_Err = components.RecipeCardSmall(recipe).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, " ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = components.ContentCardSmall("View full history...", "/v1/web/history").Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
} else {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "<p class=\"text-sm\">You have not made any recipes. There is nothing to show.</p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = components.ContentCardSmall("View full history...", "/v1/web/history").Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "</div>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "</div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
} else {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "<div class=\"my-2 mx-4 text-gray-800\"><a class=\"underline\" href=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "<div class=\"my-2 mx-4 text-gray-800\"><a class=\"underline\" href=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@ -231,12 +253,12 @@ func listsSection(loggedIn bool, viewed, made []domainRecipe.Recipe) templ.Compo
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "\"><p class=\"text-sm\">Log in to view metrics.</p></a></div>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "\"><p class=\"text-sm\">Log in to view metrics.</p></a></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "</div></section>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "</div></section>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@ -265,7 +287,7 @@ func ctaSection() templ.Component {
|
||||
templ_7745c5c3_Var7 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "<section class=\"w-full flex flex-col items-center justify-center mt-16 py-8 md:py-12 bg-gradient-to-br from-blue-100 to-purple-100 text-center\"><h2 class=\"text-2xl md:text-3xl font-extrabold text-gray-800 mb-6 px-4\">Unleash Your Inner Chef!</h2><p class=\"text-md md:text-lg text-gray-700 max-w-2xl mb-10 px-4 leading-relaxed\">Have a unique recipe idea? Want to share your culinary masterpiece with the world? It's time to bring your creations to life!</p><a href=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "<section class=\"w-full flex flex-col items-center justify-center mt-16 py-8 md:py-12 bg-gradient-to-br from-blue-100 to-purple-100 text-center\"><h2 class=\"text-2xl md:text-3xl font-extrabold text-gray-800 mb-6 px-4\">Unleash Your Inner Chef!</h2><p class=\"text-md md:text-lg text-gray-700 max-w-2xl mb-10 px-4 leading-relaxed\">Have a unique recipe idea? Want to share your culinary masterpiece with the world? It's time to bring your creations to life!</p><a href=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@ -274,7 +296,7 @@ func ctaSection() templ.Component {
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "\" class=\"flex items-center justify-center\n bg-gradient-to-r from-blue-400 to-blue-600 text-white\n px-12 py-5 rounded-full shadow-sm hover:shadow-md\n transition-all duration-300 ease-in-out shadow-blue-700\n text-lg md:text-2xl font-bold uppercase tracking-wide\">Create Your Recipe!</a></section>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "\" class=\"flex items-center justify-center\n bg-gradient-to-r from-blue-400 to-blue-600 text-white\n px-12 py-5 rounded-full shadow-sm hover:shadow-md\n transition-all duration-300 ease-in-out shadow-blue-700\n text-lg md:text-2xl font-bold uppercase tracking-wide\">Create Your Recipe!</a></section>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@ -282,7 +304,7 @@ func ctaSection() templ.Component {
|
||||
})
|
||||
}
|
||||
|
||||
func HomePage(loggedIn bool, viewed, made []domainRecipe.Recipe) templ.Component {
|
||||
func HomePage(loggedIn bool, viewed, made []domainRecipe.Recipe, recipeOfTheWeek *domainRecipe.Recipe) templ.Component {
|
||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||
@ -307,7 +329,7 @@ func HomePage(loggedIn bool, viewed, made []domainRecipe.Recipe) templ.Component
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "<div class=\"w-full h-fit flex justify-center\"><div class=\"mx-2 md:mx-0 w-full md:w-1/2 md:pt-14 h-full border-l border-r border-gray-300 bg-white\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "<div class=\"w-full h-fit flex justify-center\"><div class=\"mx-2 md:mx-0 w-full md:w-1/2 md:pt-14 h-full border-l border-r border-gray-300 bg-white\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@ -319,7 +341,7 @@ func HomePage(loggedIn bool, viewed, made []domainRecipe.Recipe) templ.Component
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = highlightSection(false).Render(ctx, templ_7745c5c3_Buffer)
|
||||
templ_7745c5c3_Err = highlightSection(recipeOfTheWeek).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@ -331,7 +353,7 @@ func HomePage(loggedIn bool, viewed, made []domainRecipe.Recipe) templ.Component
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "</div></div>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "</div></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
||||
@ -295,7 +295,7 @@ templ RecipePage(recipe domain.Recipe, user domainUser.User, loggedIn bool, doma
|
||||
@components.Navbar("")
|
||||
<div class="w-full flex justify-center">
|
||||
<div class="mx-2 md:mx-0 w-full md:w-1/2 md:pt-14 h-full border-l border-r border-gray-300 bg-white">
|
||||
<img class="bg-gray-100 w-full h-96 mx-auto mb-8" src="" alt=""/>
|
||||
<img class="bg-gray-100 w-full h-96 mx-auto mb-8" src="/v1/web/static/img/recipe_placeholder_wide.jpg" type="image/jpg"/>
|
||||
<div class="px-4 py-8 md:px-8">
|
||||
<h1 class="text-3xl md:text-4xl font-bold text-gray-800">{ recipe.Title }</h1>
|
||||
<p class="text-sm mt-2 mb-1 text-gray-700">Author: { user.Name }</p>
|
||||
|
||||
@ -803,7 +803,7 @@ func RecipePage(recipe domain.Recipe, user domainUser.User, loggedIn bool, domai
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 54, "<div class=\"w-full flex justify-center\"><div class=\"mx-2 md:mx-0 w-full md:w-1/2 md:pt-14 h-full border-l border-r border-gray-300 bg-white\"><img class=\"bg-gray-100 w-full h-96 mx-auto mb-8\" src=\"\" alt=\"\"><div class=\"px-4 py-8 md:px-8\"><h1 class=\"text-3xl md:text-4xl font-bold text-gray-800\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 54, "<div class=\"w-full flex justify-center\"><div class=\"mx-2 md:mx-0 w-full md:w-1/2 md:pt-14 h-full border-l border-r border-gray-300 bg-white\"><img class=\"bg-gray-100 w-full h-96 mx-auto mb-8\" src=\"/v1/web/static/img/recipe_placeholder_wide.jpg\" type=\"image/jpg\"><div class=\"px-4 py-8 md:px-8\"><h1 class=\"text-3xl md:text-4xl font-bold text-gray-800\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
||||
@ -43,7 +43,7 @@ templ searchResult(recipe domain.Recipe) {
|
||||
hx-swap="none"
|
||||
class="w-full p-2 border-b border-gray-200 hover:bg-gray-100 duration-200 flex items-center flex-col md:flex-row even:bg-[#f8f8f8] cursor-pointer"
|
||||
>
|
||||
<img class="bg-gray-50 size-56 md:size-40 rounded-md border-0" src=""/>
|
||||
<img class="bg-gray-50 size-56 md:size-40 rounded-md border-0" src="/v1/web/static/img/recipe_placeholder.png" type="image/png" />
|
||||
<div class="text-gray-700 p-4 flex flex-col items-center md:items-start w-full">
|
||||
<div class="flex flex-col md:flex-row items-center md:items-start justify-between w-full">
|
||||
<div class="flex flex-col items-center md:items-start">
|
||||
|
||||
@ -153,7 +153,7 @@ func searchResult(recipe domain.Recipe) templ.Component {
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "\" hx-trigger=\"click\" hx-swap=\"none\" class=\"w-full p-2 border-b border-gray-200 hover:bg-gray-100 duration-200 flex items-center flex-col md:flex-row even:bg-[#f8f8f8] cursor-pointer\"><img class=\"bg-gray-50 size-56 md:size-40 rounded-md border-0\" src=\"\"><div class=\"text-gray-700 p-4 flex flex-col items-center md:items-start w-full\"><div class=\"flex flex-col md:flex-row items-center md:items-start justify-between w-full\"><div class=\"flex flex-col items-center md:items-start\"><h3 class=\"text-xl font-semibold text-black pb-1\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "\" hx-trigger=\"click\" hx-swap=\"none\" class=\"w-full p-2 border-b border-gray-200 hover:bg-gray-100 duration-200 flex items-center flex-col md:flex-row even:bg-[#f8f8f8] cursor-pointer\"><img class=\"bg-gray-50 size-56 md:size-40 rounded-md border-0\" src=\"/v1/web/static/img/recipe_placeholder.png\" type=\"image/png\"><div class=\"text-gray-700 p-4 flex flex-col items-center md:items-start w-full\"><div class=\"flex flex-col md:flex-row items-center md:items-start justify-between w-full\"><div class=\"flex flex-col items-center md:items-start\"><h3 class=\"text-xl font-semibold text-black pb-1\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
||||
@ -9,7 +9,6 @@
|
||||
monospace;
|
||||
--color-red-100: oklch(93.6% 0.032 17.717);
|
||||
--color-red-500: oklch(63.7% 0.237 25.331);
|
||||
--color-red-800: oklch(44.4% 0.177 26.899);
|
||||
--color-green-500: oklch(72.3% 0.219 149.579);
|
||||
--color-blue-50: oklch(97% 0.014 254.604);
|
||||
--color-blue-100: oklch(93.2% 0.032 255.585);
|
||||
@ -335,9 +334,6 @@
|
||||
.mb-16 {
|
||||
margin-bottom: calc(var(--spacing) * 16);
|
||||
}
|
||||
.\[display\:-webkit-box\] {
|
||||
display: -webkit-box;
|
||||
}
|
||||
.block {
|
||||
display: block;
|
||||
}
|
||||
@ -416,6 +412,9 @@
|
||||
.h-screen {
|
||||
height: 100vh;
|
||||
}
|
||||
.min-h-72 {
|
||||
min-height: calc(var(--spacing) * 72);
|
||||
}
|
||||
.min-h-screen {
|
||||
min-height: 100vh;
|
||||
}
|
||||
@ -446,6 +445,9 @@
|
||||
.w-52 {
|
||||
width: calc(var(--spacing) * 52);
|
||||
}
|
||||
.w-80 {
|
||||
width: calc(var(--spacing) * 80);
|
||||
}
|
||||
.w-fit {
|
||||
width: fit-content;
|
||||
}
|
||||
@ -831,6 +833,9 @@
|
||||
--tw-tracking: var(--tracking-wide);
|
||||
letter-spacing: var(--tracking-wide);
|
||||
}
|
||||
.text-wrap {
|
||||
text-wrap: wrap;
|
||||
}
|
||||
.text-ellipsis {
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
@ -873,9 +878,6 @@
|
||||
.text-red-500 {
|
||||
color: var(--color-red-500);
|
||||
}
|
||||
.text-red-800 {
|
||||
color: var(--color-red-800);
|
||||
}
|
||||
.text-white {
|
||||
color: var(--color-white);
|
||||
}
|
||||
@ -971,12 +973,6 @@
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
.\[-webkit-box-orient\:vertical\] {
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
.\[-webkit-line-clamp\:4\] {
|
||||
-webkit-line-clamp: 4;
|
||||
}
|
||||
.peer-checked\:border-blue-600 {
|
||||
&:is(:where(.peer):checked ~ *) {
|
||||
border-color: var(--color-blue-600);
|
||||
@ -1307,12 +1303,6 @@
|
||||
height: calc(var(--spacing) * 48);
|
||||
}
|
||||
}
|
||||
.md\:size-64 {
|
||||
@media (width >= 48rem) {
|
||||
width: calc(var(--spacing) * 64);
|
||||
height: calc(var(--spacing) * 64);
|
||||
}
|
||||
}
|
||||
.md\:h-24 {
|
||||
@media (width >= 48rem) {
|
||||
height: calc(var(--spacing) * 24);
|
||||
@ -1333,11 +1323,6 @@
|
||||
width: calc(1/4 * 100%);
|
||||
}
|
||||
}
|
||||
.md\:w-2\/5 {
|
||||
@media (width >= 48rem) {
|
||||
width: calc(2/5 * 100%);
|
||||
}
|
||||
}
|
||||
.md\:w-3\/4 {
|
||||
@media (width >= 48rem) {
|
||||
width: calc(3/4 * 100%);
|
||||
|
||||
BIN
web/static/img/recipe_placeholder.png
Normal file
BIN
web/static/img/recipe_placeholder.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.0 KiB |
BIN
web/static/img/recipe_placeholder_wide.jpg
Normal file
BIN
web/static/img/recipe_placeholder_wide.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.7 KiB |
Loading…
x
Reference in New Issue
Block a user