From 3b6ebd0dae40f6bf21241e74ebe75aa105e33694 Mon Sep 17 00:00:00 2001 From: Hayden Hargreaves Date: Sun, 13 Jul 2025 14:01:05 -0700 Subject: [PATCH] (DB/UI): Created user list API and wired to UI. The profile list will now properly display the users recipes! The favorites list does not exist yet, since there is no backend support for favoriting/saving recipes. So the list displays the same content as the user recipe list. Same goes for the activity list, not yet implemented. --- internal/app/handlers/page_handler.go | 11 +- internal/app/handlers/user_handler.go | 48 +++ internal/app/server/server.go | 1 + internal/app/service/recipe_service.go | 8 +- internal/domain/recipe/repository.go | 2 + internal/domain/recipe/service.go | 1 + .../database/repository/recipe_repository.go | 171 ++++++-- internal/templates/pages/profile.templ | 102 +++-- internal/templates/pages/profile_templ.go | 364 +++++++++++++----- 9 files changed, 545 insertions(+), 163 deletions(-) diff --git a/internal/app/handlers/page_handler.go b/internal/app/handlers/page_handler.go index b8b2885..d375b78 100644 --- a/internal/app/handlers/page_handler.go +++ b/internal/app/handlers/page_handler.go @@ -58,9 +58,18 @@ func ProfilePage(ctx *gin.Context) { // Else, get the user data deps := ctx.MustGet("deps").(*domainServer.InjectedDependencies) user := deps.UserService.GetAuthenicatedUser(ctx) + recipes, err := deps.RecipeService.GetUserRecipes(user.Id) + if err != nil { + fmt.Printf("Error getting recipes. %s\n", err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{ + "status": http.StatusInternalServerError, + "message": fmt.Sprintf("Error getting recipes. %s\n", err.Error()), + }) + return + } title := "Potion - Profile" - page := pages.ProfilePage(user) + page := pages.ProfilePage(user, recipes) ctx.HTML(http.StatusOK, "", layouts.AppLayout(title, page)) } diff --git a/internal/app/handlers/user_handler.go b/internal/app/handlers/user_handler.go index 5ac8282..261e199 100644 --- a/internal/app/handlers/user_handler.go +++ b/internal/app/handlers/user_handler.go @@ -1 +1,49 @@ package handlers + +import ( + "fmt" + "net/http" + + "github.com/gin-gonic/gin" + domain "github.com/haydenhargreaves/Potion/internal/domain/server" +) + +func GetUserRecipes(ctx *gin.Context) { + deps := ctx.MustGet("deps").(*domain.InjectedDependencies) + + // Ensure logged in + if !domain.IsLoggedIn(ctx) { + ctx.JSON(http.StatusUnauthorized, gin.H{ + "status": http.StatusUnauthorized, + "message": "User is not authorized to access this endpoint. Please login to continue.", + "recipes": nil, + }) + return + } + + userId, ok := ctx.MustGet("userId").(int) + if !ok { + ctx.JSON(http.StatusInternalServerError, gin.H{ + "status": http.StatusInternalServerError, + "message": "Unable to access user id from store.", + "recipes": nil, + }) + return + } + + recipes, err := deps.RecipeService.GetUserRecipes(userId) + if err != nil { + ctx.JSON(http.StatusBadRequest, gin.H{ + "status": http.StatusBadRequest, + "message": fmt.Sprintf("Could not get user recipes. %s", err.Error()), + "recipes": nil, + }) + return + } + + ctx.JSON(http.StatusOK, gin.H{ + "status": http.StatusOK, + "message": "User recipes successfully retrieved.", + "recipes": recipes, + }) +} diff --git a/internal/app/server/server.go b/internal/app/server/server.go index f46e8bd..406169b 100644 --- a/internal/app/server/server.go +++ b/internal/app/server/server.go @@ -181,6 +181,7 @@ func (s *Server) Setup() *Server { // Recipe endpoints router_api.POST("/recipe", handlers.CreateRecipe) router_api.POST("/recipe/search", handlers.SearchRecipes) + router_api.GET("/user/recipes", handlers.GetUserRecipes) // Catch un-routed URLS s.Router.NoRoute(func(ctx *gin.Context) { diff --git a/internal/app/service/recipe_service.go b/internal/app/service/recipe_service.go index 82e9696..870f053 100644 --- a/internal/app/service/recipe_service.go +++ b/internal/app/service/recipe_service.go @@ -134,8 +134,8 @@ func (s *RecipeService) GetRecipe(id int) (*domain.Recipe, error) { } // SearchRecipes will search the database using the filters provided. The recipes can be passed into -// a template and displayed in the UI as the search result. A more detailed definition of the -// filters is provided below. +// a template and displayed in the UI as the search result. A more detailed definition of the +// filters is provided below. // // Each input is given a bit value (e.g., 00001 for 1) and will be passed // back to this handler as an array. The values are then added together @@ -150,3 +150,7 @@ func (s *RecipeService) GetRecipe(id int) (*domain.Recipe, error) { func (s *RecipeService) SearchRecipes(filters domain.SearchFilters) ([]domain.Recipe, error) { return s.recipeRepository.SearchRecipes(filters) } + +func (s *RecipeService) GetUserRecipes(id int) ([]domain.Recipe, error) { + return s.recipeRepository.GetUserRecipes(id) +} diff --git a/internal/domain/recipe/repository.go b/internal/domain/recipe/repository.go index b47ccd9..a5c57d9 100644 --- a/internal/domain/recipe/repository.go +++ b/internal/domain/recipe/repository.go @@ -5,4 +5,6 @@ type RecipeRepository interface { GetRecipe(id int) (*Recipe, error) SearchRecipes(filters SearchFilters) ([]Recipe, error) CreateRecipeTags(recipe Recipe, tags []string) error + GetUserRecipes(id int) ([]Recipe, error) + GetRecipeTags(recipe *Recipe) error } diff --git a/internal/domain/recipe/service.go b/internal/domain/recipe/service.go index cf6723e..b3466f4 100644 --- a/internal/domain/recipe/service.go +++ b/internal/domain/recipe/service.go @@ -6,4 +6,5 @@ type RecipeService interface { CreateRecipe(ctx *gin.Context) (*Recipe, error) GetRecipe(id int) (*Recipe, error) SearchRecipes(filters SearchFilters) ([]Recipe, error) + GetUserRecipes(id int) ([]Recipe, error) } diff --git a/internal/infrastructure/database/repository/recipe_repository.go b/internal/infrastructure/database/repository/recipe_repository.go index 293f9c8..e92739a 100644 --- a/internal/infrastructure/database/repository/recipe_repository.go +++ b/internal/infrastructure/database/repository/recipe_repository.go @@ -128,34 +128,6 @@ func (r *RecipeRepository) GetRecipe(id int) (*domain.Recipe, error) { return nil, fmt.Errorf("Failed to location recipe in database: %s", err.Error()) } - // Get tags from external tables - query = ` - SELECT t.* FROM tags t - JOIN recipetags rt ON rt.tagid = t.id - WHERE rt.recipeid = $1; - ` - rows, err := tx.Query(query, recipe.Id) - if err != nil { - return nil, fmt.Errorf("Failed to get tags for recipe. %s\n", err.Error()) - } - defer rows.Close() - - for rows.Next() { - var tag domain.Tag - - err := rows.Scan(&tag.Id, &tag.Name, &tag.Created) - if err != nil { - return nil, fmt.Errorf("Failed to scan tag onto domain model. %s\n", err.Error()) - } - - recipe.Tags = append(recipe.Tags, tag) - } - - if err := tx.Commit(); err != nil { - tx.Rollback() - return nil, err - } - // Parse duration if len(durationBytes) > 0 { var duration domain.RecipeDuration @@ -180,6 +152,14 @@ func (r *RecipeRepository) GetRecipe(id int) (*domain.Recipe, error) { recipe.Ingredients = []domain.RecipeIngredient{} } + // Add tags + r.GetRecipeTags(&recipe) + + if err := tx.Commit(); err != nil { + tx.Rollback() + return nil, err + } + return &recipe, nil } @@ -387,6 +367,9 @@ func (r *RecipeRepository) SearchRecipes(filters domain.SearchFilters) ([]domain recipe.Ingredients = []domain.RecipeIngredient{} } + // Add tags + r.GetRecipeTags(&recipe) + recipes = append(recipes, recipe) } @@ -456,3 +439,135 @@ func (r *RecipeRepository) CreateRecipeTags(recipe domain.Recipe, tags []string) return nil } + +// GetUserRecipes gets a list of a users owned 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) GetUserRecipes(id int) ([]domain.Recipe, error) { + tx, err := r.db.Begin() + if err != nil { + tx.Rollback() + return nil, err + } + + query := ` + SELECT id, title, description, instructions, serves, difficulty, duration, category, ingredients, + userid, modified, created + FROM recipes + WHERE userid = $1 + ORDER BY created DESC; + ` + + rows, err := tx.Query(query, id) + if err != nil { + return nil, fmt.Errorf("Failed to query DB for user recipes. %s\n", err.Error()) + } + defer rows.Close() + + // Prepare statement for tag query + // tagQuery := ` + // ` + + var recipes []domain.Recipe + for rows.Next() { + var recipe domain.Recipe + var durationBytes []byte + var ingredientBytes []byte + + // Scan results from recipe query onto recipe object + if err := rows.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 scan row onto recipe object. %s\n", 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 + r.GetRecipeTags(&recipe) + + recipes = append(recipes, recipe) + } + + if err := tx.Commit(); err != nil { + tx.Rollback() + return nil, err + } + + return recipes, nil +} + +// 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. +func (r *RecipeRepository) GetRecipeTags(recipe *domain.Recipe) error { + tx, err := r.db.Begin() + if err != nil { + tx.Rollback() + return err + } + + recipe.Tags = []domain.Tag{} + + query := ` + SELECT t.* FROM tags t + JOIN recipetags rt ON rt.tagid = t.id + WHERE rt.recipeid = $1; + ` + rows, err := tx.Query(query, recipe.Id) + if err != nil { + return fmt.Errorf("Failed to get tags for recipe. %s\n", err.Error()) + } + defer rows.Close() + + for rows.Next() { + var tag domain.Tag + + err := rows.Scan(&tag.Id, &tag.Name, &tag.Created) + if err != nil { + return fmt.Errorf("Failed to scan tag onto domain model. %s\n", err.Error()) + } + + recipe.Tags = append(recipe.Tags, tag) + } + + if err := tx.Commit(); err != nil { + tx.Rollback() + return err + } + + return nil +} diff --git a/internal/templates/pages/profile.templ b/internal/templates/pages/profile.templ index 51a406b..c631efa 100644 --- a/internal/templates/pages/profile.templ +++ b/internal/templates/pages/profile.templ @@ -1,10 +1,38 @@ package templates import "github.com/haydenhargreaves/Potion/internal/templates/components" +import "fmt" +import "strings" import domain "github.com/haydenhargreaves/Potion/internal/domain/server" -import domain_user "github.com/haydenhargreaves/Potion/internal/domain/user" +import domainRecipe "github.com/haydenhargreaves/Potion/internal/domain/recipe" +import domainUser "github.com/haydenhargreaves/Potion/internal/domain/user" -templ userDetailsSection(user domain_user.User) { +func displayDifficulty(diff int) string { + switch diff { + case 1: + return "Beginner" + case 2: + return "Easy" + case 3: + return "Intermediate" + case 4: + return "Challenging" + case 5: + return "Extreme" + default: + return "" + } +} + +func displayTags(tags []domainRecipe.Tag) string { + names := make([]string, 0, len(tags)) + for _, tag := range tags { + names = append(names, tag.Name) + } + return strings.Join(names, ", ") +} + +templ userDetailsSection(user domainUser.User, recipeCount int) {
{ user.Email }

-

10 recipes

-

14 favorites

+

{ recipeCount } recipes

+

0 favorites

} -templ recipesSection() { +templ recipesSection(recipes []domainRecipe.Recipe) {

My Recipes

} -templ ProfilePage(user domain_user.User) { +templ ProfilePage(user domainUser.User, recipes []domainRecipe.Recipe) { @components.Navbar(" profile")
- @userDetailsSection(user) - @recipesSection() - @favoritesSection() + @userDetailsSection(user, len(recipes)) + @recipesSection(recipes) + @favoritesSection(recipes) @activitySection() @logoutSection()
diff --git a/internal/templates/pages/profile_templ.go b/internal/templates/pages/profile_templ.go index a116777..6ea849b 100644 --- a/internal/templates/pages/profile_templ.go +++ b/internal/templates/pages/profile_templ.go @@ -9,10 +9,38 @@ import "github.com/a-h/templ" import templruntime "github.com/a-h/templ/runtime" import "github.com/haydenhargreaves/Potion/internal/templates/components" +import "fmt" +import "strings" import domain "github.com/haydenhargreaves/Potion/internal/domain/server" -import domain_user "github.com/haydenhargreaves/Potion/internal/domain/user" +import domainRecipe "github.com/haydenhargreaves/Potion/internal/domain/recipe" +import domainUser "github.com/haydenhargreaves/Potion/internal/domain/user" -func userDetailsSection(user domain_user.User) templ.Component { +func displayDifficulty(diff int) string { + switch diff { + case 1: + return "Beginner" + case 2: + return "Easy" + case 3: + return "Intermediate" + case 4: + return "Challenging" + case 5: + return "Extreme" + default: + return "" + } +} + +func displayTags(tags []domainRecipe.Tag) string { + names := make([]string, 0, len(tags)) + for _, tag := range tags { + names = append(names, tag.Name) + } + return strings.Join(names, ", ") +} + +func userDetailsSection(user domainUser.User, recipeCount int) 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 { @@ -40,7 +68,7 @@ func userDetailsSection(user domain_user.User) templ.Component { var templ_7745c5c3_Var2 string templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(user.ImageUrl) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 12, Col: 23} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 40, Col: 23} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2)) if templ_7745c5c3_Err != nil { @@ -53,7 +81,7 @@ func userDetailsSection(user domain_user.User) templ.Component { var templ_7745c5c3_Var3 string templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(user.Name) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 16, Col: 62} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 44, Col: 62} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3)) if templ_7745c5c3_Err != nil { @@ -66,13 +94,26 @@ func userDetailsSection(user domain_user.User) templ.Component { var templ_7745c5c3_Var4 string templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(user.Email) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 17, Col: 47} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 45, Col: 47} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "

10 recipes

14 favorites

") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var5 string + templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(recipeCount) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 48, Col: 72} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, " recipes

0 favorites

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -80,56 +121,7 @@ func userDetailsSection(user domain_user.User) templ.Component { }) } -func recipesSection() 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 { - return templ_7745c5c3_CtxErr - } - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) - if !templ_7745c5c3_IsBuffer { - defer func() { - templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) - if templ_7745c5c3_Err == nil { - templ_7745c5c3_Err = templ_7745c5c3_BufErr - } - }() - } - ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var5 := templ.GetChildren(ctx) - if templ_7745c5c3_Var5 == nil { - templ_7745c5c3_Var5 = templ.NopComponent - } - ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "

My Recipes

") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - return nil - }) -} - -func favoritesSection() templ.Component { +func recipesSection(recipes []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 { @@ -150,36 +142,83 @@ func favoritesSection() templ.Component { templ_7745c5c3_Var6 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "

My Favorites

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -203,12 +242,12 @@ func activitySection() templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var8 := templ.GetChildren(ctx) - if templ_7745c5c3_Var8 == nil { - templ_7745c5c3_Var8 = templ.NopComponent + templ_7745c5c3_Var9 := templ.GetChildren(ctx) + if templ_7745c5c3_Var9 == nil { + templ_7745c5c3_Var9 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "

My Favorites

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -244,7 +283,7 @@ func activitySection() templ.Component { }) } -func recipeListItem() templ.Component { +func recipeListItem(recipe 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 { @@ -260,12 +299,135 @@ func recipeListItem() templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var9 := templ.GetChildren(ctx) - if templ_7745c5c3_Var9 == nil { - templ_7745c5c3_Var9 = templ.NopComponent + templ_7745c5c3_Var10 := templ.GetChildren(ctx) + if templ_7745c5c3_Var10 == nil { + templ_7745c5c3_Var10 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "
  • My Awesome Chili Recipe

    Difficulty: Medium | Duration: 60 min | Category: Dinner

    Difficulty: Medium

    Duration: 60 min

    Category: Dinner

    Tags: comfort food, spicy, beef

  • ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "
  • ") + 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/pages/profile.templ`, Line: 129, Col: 18} + } + _, 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, 15, "

    Difficulty: ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var13 string + templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(displayDifficulty(recipe.Difficulty)) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 133, Col: 81} + } + _, 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, 16, " | Duration: ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var14 string + templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(recipe.Duration.Total) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 134, Col: 66} + } + _, 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, 17, " min | Category: ") + 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/pages/profile.templ`, Line: 135, Col: 60} + } + _, 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, 18, "

    Difficulty: ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var16 string + templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(displayDifficulty(recipe.Difficulty)) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 138, Col: 81} + } + _, 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, 19, "

    Duration: ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var17 string + templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(recipe.Duration.Total) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 141, Col: 64} + } + _, 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, 20, " min

    Category: ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var18 string + templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(recipe.Category) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 144, Col: 58} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "

    ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + if len(recipe.Tags) > 0 { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "

    Tags: ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var19 string + templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(displayTags(recipe.Tags)) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 148, Col: 36} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "

    ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "
  • ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -289,12 +451,12 @@ func activityListItem() templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var10 := templ.GetChildren(ctx) - if templ_7745c5c3_Var10 == nil { - templ_7745c5c3_Var10 = templ.NopComponent + templ_7745c5c3_Var20 := templ.GetChildren(ctx) + if templ_7745c5c3_Var20 == nil { + templ_7745c5c3_Var20 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "
  • Rated \"Spicy Chicken Wings\"

    2 days ago

  • ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "
  • Rated \"Spicy Chicken Wings\"

    2 days ago

  • ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -318,21 +480,21 @@ func logoutSection() templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var11 := templ.GetChildren(ctx) - if templ_7745c5c3_Var11 == nil { - templ_7745c5c3_Var11 = templ.NopComponent + templ_7745c5c3_Var21 := templ.GetChildren(ctx) + if templ_7745c5c3_Var21 == nil { + templ_7745c5c3_Var21 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "
    Logout
    ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "\" class=\"text-center border border-red-500 text-red-500 w-9/10 md:w-1/3 py-2 rounded-lg hover:cursor-pointer hover:bg-red-100 duration-300\">Logout") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -340,7 +502,7 @@ func logoutSection() templ.Component { }) } -func ProfilePage(user domain_user.User) templ.Component { +func ProfilePage(user domainUser.User, recipes []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 { @@ -356,28 +518,28 @@ func ProfilePage(user domain_user.User) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var13 := templ.GetChildren(ctx) - if templ_7745c5c3_Var13 == nil { - templ_7745c5c3_Var13 = templ.NopComponent + templ_7745c5c3_Var23 := templ.GetChildren(ctx) + if templ_7745c5c3_Var23 == nil { + templ_7745c5c3_Var23 = templ.NopComponent } ctx = templ.ClearChildren(ctx) templ_7745c5c3_Err = components.Navbar(" profile").Render(ctx, templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "
    ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "
    ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = userDetailsSection(user).Render(ctx, templ_7745c5c3_Buffer) + templ_7745c5c3_Err = userDetailsSection(user, len(recipes)).Render(ctx, templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = recipesSection().Render(ctx, templ_7745c5c3_Buffer) + templ_7745c5c3_Err = recipesSection(recipes).Render(ctx, templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = favoritesSection().Render(ctx, templ_7745c5c3_Buffer) + templ_7745c5c3_Err = favoritesSection(recipes).Render(ctx, templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -389,7 +551,7 @@ func ProfilePage(user domain_user.User) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "
    ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "
    ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err }