Potion/internal/app/handlers/page_handler.go
Hayden Hargreaves 21f958a4fb (FEAT): Completed the lists section on the home page.
This includes backend updates as well as frontend changes! The backend
also includes a new repository to get a list of jobs via their IDs,
which does respect order!

The list displays for users that are logged in and a small message when
the user is not logged in. The last piece is the recipe of the week
segment, which can be as simple as a DB cron-job. But that will require
stored procedures. Need to learn those next.

Though I want to deploy the application soon, so I need to begin working
on that.
2025-07-20 21:56:56 -07:00

252 lines
7.2 KiB
Go

package handlers
import (
"encoding/json"
"fmt"
"net/http"
"strconv"
"github.com/a-h/templ"
"github.com/gin-gonic/gin"
domainRecipe "github.com/haydenhargreaves/Potion/internal/domain/recipe"
domain "github.com/haydenhargreaves/Potion/internal/domain/server"
domainServer "github.com/haydenhargreaves/Potion/internal/domain/server"
layouts "github.com/haydenhargreaves/Potion/internal/templates/layouts"
pages "github.com/haydenhargreaves/Potion/internal/templates/pages"
templates "github.com/haydenhargreaves/Potion/internal/templates/pages"
)
func LoginPage(ctx *gin.Context) {
title := "Potion - Login"
page := pages.LoginPage()
ctx.HTML(http.StatusOK, "", layouts.AppLayout(title, page))
}
func HomePage(ctx *gin.Context) {
deps := ctx.MustGet("deps").(*domainServer.InjectedDependencies)
loggedIn := domain.IsLoggedIn(ctx)
var page templ.Component
if loggedIn {
userId := ctx.MustGet("userId").(int)
madeRecipes, err := deps.RecipeService.GetUserMadeRecipes(userId, 6)
if err != nil {
ctx.JSON(http.StatusInternalServerError, gin.H{
"status": http.StatusInternalServerError,
"message": fmt.Sprintf("Error getting made recipes. %s\n", err.Error()),
})
return
}
viewedRecipes, err := deps.RecipeService.GetUserViewedRecipes(userId, 6)
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)
} else {
page = templates.HomePage(false, nil, nil)
}
title := "Potion - Home"
ctx.HTML(http.StatusOK, "", layouts.AppLayout(title, page))
}
func FavoritesPage(ctx *gin.Context) {
// If not logged in, direct to the login page
if !domainServer.IsLoggedIn(ctx) {
ctx.Redirect(http.StatusSeeOther, domainServer.WEB_LOGIN)
return
}
title := "Potion - Favorites"
var page templ.Component
// Get filters from cookies
if bytes, err := ctx.Cookie("search-filters"); err != nil {
fmt.Printf("ERROR: Failed to get search-filter cookie. %s\n", err.Error())
page = pages.FavoritesPage(domainRecipe.SearchFilters{})
} else {
var filters domainRecipe.SearchFilters
if err := json.Unmarshal([]byte(bytes), &filters); err != nil {
fmt.Printf("ERROR: Failed to unmarshal search-filter cookie. %s\n", err.Error())
page = pages.FavoritesPage(domainRecipe.SearchFilters{})
} else {
page = pages.FavoritesPage(filters)
}
}
// Else, get the user's favorites
// BUG: Depreciated, not displaying a list, using search to drive this page as well
// deps := ctx.MustGet("deps").(*domainServer.InjectedDependencies)
// userId := ctx.MustGet("userId").(int)
// recipes, err := deps.RecipeService.GetUserFavoriteRecipes(userId)
// if err != nil {
// ctx.JSON(http.StatusInternalServerError, gin.H{
// "status": http.StatusInternalServerError,
// "message": fmt.Sprintf("Error getting favorites. %s\n", err.Error()),
// })
// return
// }
ctx.HTML(http.StatusOK, "", layouts.AppLayout(title, page))
}
func CreatePage(ctx *gin.Context) {
// If not logged in, direct to the login page
if !domainServer.IsLoggedIn(ctx) {
ctx.Redirect(http.StatusSeeOther, domainServer.WEB_LOGIN)
return
}
title := "Potion - Create"
page := pages.CreatePage()
ctx.HTML(http.StatusOK, "", layouts.AppLayout(title, page))
}
func ProfilePage(ctx *gin.Context) {
// If not logged in, direct to the login page
if !domainServer.IsLoggedIn(ctx) {
ctx.Redirect(http.StatusSeeOther, domainServer.WEB_LOGIN)
return
}
// 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 {
ctx.JSON(http.StatusInternalServerError, gin.H{
"status": http.StatusInternalServerError,
"message": fmt.Sprintf("Error getting recipes. %s\n", err.Error()),
})
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 {
ctx.JSON(http.StatusInternalServerError, gin.H{
"status": http.StatusInternalServerError,
"message": fmt.Sprintf("Error getting user engagements. %s\n", err.Error()),
})
return
}
title := "Potion - Profile"
page := pages.ProfilePage(user, recipes, favorites, engagements)
ctx.HTML(http.StatusOK, "", layouts.AppLayout(title, page))
}
func ListPage(ctx *gin.Context) {
title := "Potion - Shopping List"
page := pages.ListPage()
ctx.HTML(http.StatusOK, "", layouts.AppLayout(title, page))
}
// TODO: Figure out how to handle errors, think we just need a simple display.
func RecipePage(ctx *gin.Context) {
// Call recipe service to get via ID
deps := ctx.MustGet("deps").(*domainServer.InjectedDependencies)
id := ctx.Param("id")
// Parse ID
parsed, err := strconv.Atoi(id)
if err != nil {
fmt.Printf("ERROR: %s\n", err.Error())
ctx.JSON(400, err.Error())
return
}
// Get signed in user, if they exist
var userId *int = nil
var loggedIn = domainServer.IsLoggedIn(ctx)
if loggedIn {
storeId := ctx.MustGet("userId").(int)
userId = &storeId
}
// Get recipe
recipe, err := deps.RecipeService.GetRecipe(parsed, userId)
if err != nil {
fmt.Printf("ERROR: %s\n", err.Error())
ctx.JSON(400, err.Error())
return
}
// Get user (owner)
user, err := deps.UserService.GetUser(recipe.UserId)
if err != nil {
fmt.Printf("ERROR: %s\n", err.Error())
ctx.JSON(400, err.Error())
return
}
// Add engagement
// BUG: Don't want to do this here
// if loggedIn {
// if _, err = deps.EngagementService.UserViewRecipe(*userId, recipe.Id); err != nil {
// fmt.Printf("ERROR: %s\n", err.Error())
// ctx.JSON(400, err.Error())
// return
// }
// } else {
// if _, err = deps.EngagementService.ViewRecipe(recipe.Id); err != nil {
// fmt.Printf("ERROR: %s\n", err.Error())
// ctx.JSON(400, err.Error())
// return
// }
// }
title := "Potion - View Recipe"
page := pages.RecipePage(*recipe, *user, loggedIn)
ctx.HTML(http.StatusOK, "", layouts.AppLayout(title, page))
}
func SearchPage(ctx *gin.Context) {
var page templ.Component
// Get filters from cookies
if bytes, err := ctx.Cookie("search-filters"); err != nil {
fmt.Printf("ERROR: Failed to get search-filter cookie. %s\n", err.Error())
page = pages.SearchPage(domainRecipe.SearchFilters{}, false)
} else {
var filters domainRecipe.SearchFilters
if err := json.Unmarshal([]byte(bytes), &filters); err != nil {
fmt.Printf("ERROR: Failed to unmarshal search-filter cookie. %s\n", err.Error())
page = pages.SearchPage(domainRecipe.SearchFilters{}, false)
} else {
page = pages.SearchPage(filters, true)
}
}
title := "Potion - Recipe Search"
ctx.HTML(http.StatusOK, "", layouts.AppLayout(title, page))
}
func NotFoundPage(ctx *gin.Context) {
title := "Potion - Not Found"
page := pages.NotFoundPage()
ctx.HTML(http.StatusOK, "", layouts.AppLayout(title, page))
}