FEATURE: Favorites Page Implementation #22

Merged
azpect merged 3 commits from feature/favorites into master 2025-07-16 19:41:30 -07:00
10 changed files with 234 additions and 51 deletions
Showing only changes of commit aacca77890 - Show all commits

View File

@ -67,6 +67,15 @@ func ProfilePage(ctx *gin.Context) {
return
}
favorites, err := deps.RecipeService.GetUserFavoriteRecipes(user.Id)
if err != nil {
ctx.JSON(http.StatusInternalServerError, gin.H{
"status": http.StatusInternalServerError,
"message": fmt.Sprintf("Error getting recipes. %s\n", err.Error()),
})
return
}
// Get the engagement data, not sure what will happen when errors occur
engagements, err := deps.EngagementService.GetUserEngagement(user.Id, 6)
if err != nil {
@ -78,7 +87,7 @@ func ProfilePage(ctx *gin.Context) {
}
title := "Potion - Profile"
page := pages.ProfilePage(user, recipes, engagements)
page := pages.ProfilePage(user, recipes, favorites, engagements)
ctx.HTML(http.StatusOK, "", layouts.AppLayout(title, page))
}

View File

@ -47,3 +47,43 @@ func GetUserRecipes(ctx *gin.Context) {
"recipes": recipes,
})
}
func GetUserFavoriteRecipes(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.GetUserFavoriteRecipes(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,
})
}

View File

@ -185,6 +185,7 @@ func (s *Server) Setup() *Server {
router_api.POST("/recipe", handlers.CreateRecipe)
router_api.POST("/recipe/search", handlers.SearchRecipes)
router_api.GET("/user/recipes", handlers.GetUserRecipes)
router_api.GET("/user/favorites", handlers.GetUserFavoriteRecipes)
// Engagement endpoints
router_api.POST("/engagement/view/:id", handlers.EngagementViewRecipe)

View File

@ -155,6 +155,14 @@ func (s *RecipeService) SearchRecipes(filters domain.SearchFilters) ([]domain.Re
return s.recipeRepository.SearchRecipes(filters)
}
// GetUserRecipes returns a list of the recipes that the user has created. The user's
// ID should be provided. Any errors will be bubbled to the caller.
func (s *RecipeService) GetUserRecipes(id int) ([]domain.Recipe, error) {
return s.recipeRepository.GetUserRecipes(id)
}
// GetUserFavoriteRecipes returns a list of the recipes that the user has marked as a
// favorite. The user's ID should be provided. Any errors will be bubbled to the caller.
func (s *RecipeService) GetUserFavoriteRecipes(id int) ([]domain.Recipe, error) {
return s.recipeRepository.GetUserFavoriteRecipes(id)
}

View File

@ -6,6 +6,7 @@ type RecipeRepository interface {
SearchRecipes(filters SearchFilters) ([]Recipe, error)
CreateRecipeTags(recipe Recipe, tags []string) error
GetUserRecipes(id int) ([]Recipe, error)
GetUserFavoriteRecipes(id int) ([]Recipe, error)
GetRecipeTags(recipe *Recipe) error
GetRecipeFavorite(recipe *Recipe, userId int) error
}

View File

@ -7,4 +7,5 @@ type RecipeService interface {
GetRecipe(id int, userId *int) (*Recipe, error)
SearchRecipes(filters SearchFilters) ([]Recipe, error)
GetUserRecipes(id int) ([]Recipe, error)
GetUserFavoriteRecipes(id int) ([]Recipe, error)
}

View File

@ -548,6 +548,94 @@ func (r *RecipeRepository) GetUserRecipes(id int) ([]domain.Recipe, error) {
return recipes, nil
}
func (r *RecipeRepository) GetUserFavoriteRecipes(id 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 favorites f
JOIN recipes r ON r.id = f.recipeid
WHERE f.userid = $1
ORDER BY f.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()
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
if err := r.GetRecipeTags(&recipe); err != nil {
fmt.Printf("ERROR getting recipe tags. %s\n", err.Error())
}
// Set favorite status (they're always true!)
recipe.Favorite = true
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.

View File

@ -44,7 +44,7 @@ templ userDetailsSection(user domainUser.User, recipeCount int) {
} else {
<img
class="w-24 md:w-32 border-2 border-blue-500 rounded-full shadow-blue-500 shadow select-none"
src={ fmt.Sprintf("https://ui-avatars.com/api/?name=%s+%s&size=150", strings.Split(user.Name, " " )[0], strings.Split(user.Name, " " )[1]) }
src={ fmt.Sprintf("https://ui-avatars.com/api/?name=%s+%s&size=150", strings.Split(user.Name, " ")[0], strings.Split(user.Name, " ")[1]) }
/>
}
<div class="flex flex-col gap-y-4">
@ -74,7 +74,7 @@ templ recipesSection(recipes []domainRecipe.Recipe) {
@recipeListItem(recipe)
}
}
<a href="/" class="bg-red-500">
<a href="/">
<li
class="w-full border-b border-gray-300 px-2 py-4 even:bg-gray-50 hover:bg-gray-100 hover:text-blue-600 duration-150 text-center"
>
@ -88,7 +88,24 @@ templ recipesSection(recipes []domainRecipe.Recipe) {
templ favoritesSection(recipes []domainRecipe.Recipe) {
<section class="p-8">
<h2 class="text-2xl font-semibold text-gray-800">My Favorites</h2>
<p class="text-sm my-2">Favorites section is under construction!</p>
<ul class="w-full my-2">
if len(recipes) <= 4 {
for _, recipe := range recipes {
@recipeListItem(recipe)
}
} else {
for _, recipe := range recipes[:4] {
@recipeListItem(recipe)
}
}
<a href="/">
<li
class="w-full border-b border-gray-300 px-2 py-4 even:bg-gray-50 hover:bg-gray-100 hover:text-blue-600 duration-150 text-center"
>
See all...
</li>
</a>
</ul>
</section>
}
@ -113,13 +130,13 @@ templ activitySection(engagement []domainEngagement.Engagement) {
templ recipeListItem(recipe domainRecipe.Recipe) {
<li class="w-full border-b border-gray-300 px-2 py-4 even:bg-gray-50 hover:bg-gray-100 duration-150">
<p
hx-post={ fmt.Sprintf(domain.API_ENGAGEMENT_VIEW, recipe.Id) }
hx-trigger="click"
hx-swap="none"
class="text-base md:text-lg hover:text-blue-600 duration-100 cursor-pointer"
>
{ recipe.Title }
<p
hx-post={ fmt.Sprintf(domain.API_ENGAGEMENT_VIEW, recipe.Id) }
hx-trigger="click"
hx-swap="none"
class="text-base md:text-lg hover:text-blue-600 duration-100 cursor-pointer"
>
{ recipe.Title }
</p>
<p class="hidden md:block text-sm text-gray-700 my-1.5">
Difficulty: <span class="font-semibold">{ displayDifficulty(recipe.Difficulty) }</span>
@ -167,13 +184,13 @@ templ logoutSection() {
</section>
}
templ ProfilePage(user domainUser.User, recipes []domainRecipe.Recipe, engagement []domainEngagement.Engagement) {
templ ProfilePage(user domainUser.User, recipes []domainRecipe.Recipe, favorites []domainRecipe.Recipe, engagement []domainEngagement.Engagement) {
@components.Navbar(" profile")
<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 border-l border-r border-gray-300 bg-white flex flex-col">
@userDetailsSection(user, len(recipes))
@recipesSection(recipes)
@favoritesSection(recipes)
@favoritesSection(favorites)
@activitySection(engagement)
@logoutSection()
</div>

View File

@ -92,7 +92,7 @@ func userDetailsSection(user domainUser.User, recipeCount int) templ.Component {
var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("https://ui-avatars.com/api/?name=%s+%s&size=150", strings.Split(user.Name, " ")[0], strings.Split(user.Name, " ")[1]))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 47, Col: 143}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 47, Col: 141}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil {
@ -190,7 +190,7 @@ func recipesSection(recipes []domainRecipe.Recipe) templ.Component {
}
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "<a href=\"/\" class=\"bg-red-500\"><li class=\"w-full border-b border-gray-300 px-2 py-4 even:bg-gray-50 hover:bg-gray-100 hover:text-blue-600 duration-150 text-center\">See all...</li></a></ul></section>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "<a href=\"/\"><li class=\"w-full border-b border-gray-300 px-2 py-4 even:bg-gray-50 hover:bg-gray-100 hover:text-blue-600 duration-150 text-center\">See all...</li></a></ul></section>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -219,7 +219,26 @@ func favoritesSection(recipes []domainRecipe.Recipe) templ.Component {
templ_7745c5c3_Var8 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "<section class=\"p-8\"><h2 class=\"text-2xl font-semibold text-gray-800\">My Favorites</h2><p class=\"text-sm my-2\">Favorites section is under construction!</p></section>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "<section class=\"p-8\"><h2 class=\"text-2xl font-semibold text-gray-800\">My Favorites</h2><ul class=\"w-full my-2\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if len(recipes) <= 4 {
for _, recipe := range recipes {
templ_7745c5c3_Err = recipeListItem(recipe).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
} else {
for _, recipe := range recipes[:4] {
templ_7745c5c3_Err = recipeListItem(recipe).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "<a href=\"/\"><li class=\"w-full border-b border-gray-300 px-2 py-4 even:bg-gray-50 hover:bg-gray-100 hover:text-blue-600 duration-150 text-center\">See all...</li></a></ul></section>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -248,7 +267,7 @@ func activitySection(engagement []domainEngagement.Engagement) templ.Component {
templ_7745c5c3_Var9 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "<section class=\"p-8\"><h2 class=\"text-2xl font-semibold text-gray-800\">Recent Activity</h2><p class=\"text-sm my-2\">Activity section is under construction!</p><ul class=\"w-full my-2\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "<section class=\"p-8\"><h2 class=\"text-2xl font-semibold text-gray-800\">Recent Activity</h2><p class=\"text-sm my-2\">Activity section is under construction!</p><ul class=\"w-full my-2\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -258,7 +277,7 @@ func activitySection(engagement []domainEngagement.Engagement) templ.Component {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "<a href=\"/\" class=\"bg-red-500\"><li class=\"w-full border-b border-gray-300 px-2 py-4 even:bg-gray-50 hover:bg-gray-100 hover:text-blue-600 duration-150 text-center\">See all...</li></a></ul></section>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "<a href=\"/\" class=\"bg-red-500\"><li class=\"w-full border-b border-gray-300 px-2 py-4 even:bg-gray-50 hover:bg-gray-100 hover:text-blue-600 duration-150 text-center\">See all...</li></a></ul></section>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -287,134 +306,134 @@ func recipeListItem(recipe domainRecipe.Recipe) templ.Component {
templ_7745c5c3_Var10 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "<li class=\"w-full border-b border-gray-300 px-2 py-4 even:bg-gray-50 hover:bg-gray-100 duration-150\"><p hx-post=\"")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "<li class=\"w-full border-b border-gray-300 px-2 py-4 even:bg-gray-50 hover:bg-gray-100 duration-150\"><p hx-post=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var11 string
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf(domain.API_ENGAGEMENT_VIEW, recipe.Id))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 117, Col: 66}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 134, Col: 63}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "\" hx-trigger=\"click\" hx-swap=\"none\" class=\"text-base md:text-lg hover:text-blue-600 duration-100 cursor-pointer\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "\" hx-trigger=\"click\" hx-swap=\"none\" class=\"text-base md:text-lg hover:text-blue-600 duration-100 cursor-pointer\">")
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: 122, Col: 18}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 139, Col: 17}
}
_, 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, 17, "</p><p class=\"hidden md:block text-sm text-gray-700 my-1.5\">Difficulty: <span class=\"font-semibold\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "</p><p class=\"hidden md:block text-sm text-gray-700 my-1.5\">Difficulty: <span class=\"font-semibold\">")
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: 125, Col: 81}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 142, 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, 18, "</span> | Duration: <span class=\"font-semibold\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "</span> | Duration: <span class=\"font-semibold\">")
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: 126, Col: 66}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 143, 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, 19, " min</span> | Category: <span class=\"font-semibold\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, " min</span> | Category: <span class=\"font-semibold\">")
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: 127, Col: 60}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 144, 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, 20, "</span></p><p class=\"md:hidden text-xs md:text-sm text-gray-700 my-1\">Difficulty: <span class=\"font-semibold\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "</span></p><p class=\"md:hidden text-xs md:text-sm text-gray-700 my-1\">Difficulty: <span class=\"font-semibold\">")
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: 130, Col: 81}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 147, 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, 21, "</span></p><p class=\"md:hidden text-xs md:text-sm text-gray-700 my-1\">Duration: <span class=\"font-semibold\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "</span></p><p class=\"md:hidden text-xs md:text-sm text-gray-700 my-1\">Duration: <span class=\"font-semibold\">")
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: 133, Col: 64}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 150, 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, 22, " min</span></p><p class=\"md:hidden text-xs md:text-sm text-gray-700 my-1\">Category: <span class=\"font-semibold\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, " min</span></p><p class=\"md:hidden text-xs md:text-sm text-gray-700 my-1\">Category: <span class=\"font-semibold\">")
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: 136, Col: 58}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 153, 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, 23, "</span></p>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "</span></p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if len(recipe.Tags) > 0 {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "<p class=\"text-xs italic text-gray-500\">Tags: ")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "<p class=\"text-xs italic text-gray-500\">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: 140, Col: 36}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 157, 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, 25, "</p>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "</p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "</li>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "</li>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -443,33 +462,33 @@ func activityListItem(engagement domainEngagement.Engagement) templ.Component {
templ_7745c5c3_Var20 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "<li class=\"w-full border-b border-gray-300 px-2 py-4 even:bg-gray-50 hover:bg-gray-100 duration-150 flex justify-between items-center\"><p class=\"text-sm md:text-base text-gray-800\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "<li class=\"w-full border-b border-gray-300 px-2 py-4 even:bg-gray-50 hover:bg-gray-100 duration-150 flex justify-between items-center\"><p class=\"text-sm md:text-base text-gray-800\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var21 string
templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinStringErrs(engagement.Message)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 151, Col: 23}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 168, Col: 23}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "</p><p class=\"text-xs md:text-sm text-gray-600 w-fit shrink-0\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "</p><p class=\"text-xs md:text-sm text-gray-600 w-fit shrink-0\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var22 string
templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinStringErrs(engagement.Created.Format("01/02/2006"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 154, Col: 44}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/profile.templ`, Line: 171, Col: 44}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "</p></li>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "</p></li>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -498,7 +517,7 @@ func logoutSection() templ.Component {
templ_7745c5c3_Var23 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "<section class=\"w-full flex flex-col justify-center items-center py-8 border-t border-gray-300 mt-auto\"><a href=\"")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "<section class=\"w-full flex flex-col justify-center items-center py-8 border-t border-gray-300 mt-auto\"><a href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -507,7 +526,7 @@ func logoutSection() templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "\" 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</a></section>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "\" 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</a></section>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -515,7 +534,7 @@ func logoutSection() templ.Component {
})
}
func ProfilePage(user domainUser.User, recipes []domainRecipe.Recipe, engagement []domainEngagement.Engagement) templ.Component {
func ProfilePage(user domainUser.User, recipes []domainRecipe.Recipe, favorites []domainRecipe.Recipe, engagement []domainEngagement.Engagement) 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 {
@ -540,7 +559,7 @@ func ProfilePage(user domainUser.User, recipes []domainRecipe.Recipe, engagement
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "<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 border-l border-r border-gray-300 bg-white flex flex-col\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "<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 border-l border-r border-gray-300 bg-white flex flex-col\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -552,7 +571,7 @@ func ProfilePage(user domainUser.User, recipes []domainRecipe.Recipe, engagement
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = favoritesSection(recipes).Render(ctx, templ_7745c5c3_Buffer)
templ_7745c5c3_Err = favoritesSection(favorites).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -564,7 +583,7 @@ func ProfilePage(user domainUser.User, recipes []domainRecipe.Recipe, engagement
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "</div></div>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "</div></div>")
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-green-300: oklch(87.1% 0.15 154.449);
--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);