FEAT: Implemented Recipe of the Week #35

Merged
azpect merged 6 commits from dev into master 2025-07-26 23:07:00 -07:00
21 changed files with 431 additions and 134 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB