Potion/internal/app/handlers/page_handler.go
Hayden Hargreaves 47b8386844 (FEAT): Errors can now be displayed using the RenderErrorBanner fx.
This is found in the components domain. Make sure only HTMX routes call
it. Although, I think I put this into the page handlers WHICH IS WRONG.

However, it does work, ish. But it does not load into the DOM properly.
But it seems to display just fine.
2025-09-03 22:22:23 -07:00

307 lines
9.7 KiB
Go
Executable File

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"
"github.com/haydenhargreaves/Potion/internal/templates/components"
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)
// Ensure user is logged in with a valid account
if user := deps.UserService.GetAuthenicatedUser(ctx); user == nil {
// Log (stale) user out
domain.SetCookie(ctx, "jwt_token", "", -1)
domain.SetCookie(ctx, "search-filters", "", -1)
loggedIn = false
}
var page templ.Component
if loggedIn {
userId := ctx.MustGet("userId").(int)
madeRecipes, err := deps.RecipeService.GetUserMadeRecipes(userId, 6)
if err != nil {
components.RenderErrorBanner(ctx, fmt.Sprintf("Error getting made recipes. %s\n", err.Error()))
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 {
components.RenderErrorBanner(ctx, fmt.Sprintf("Error getting viewed recipes. %s\n", err.Error()))
ctx.JSON(http.StatusInternalServerError, gin.H{
"status": http.StatusInternalServerError,
"message": fmt.Sprintf("Error getting viewed recipes. %s\n", err.Error()),
})
return
}
// Get the recipe of the week
recipeOfTheWeek, err := deps.RecipeService.GetRecipeOfTheWeek(&userId)
if err != nil {
components.RenderErrorBanner(ctx, fmt.Sprintf("Error getting recipe of the week. %s\n", err.Error()))
ctx.JSON(http.StatusInternalServerError, gin.H{
"status": http.StatusInternalServerError,
"message": fmt.Sprintf("Error getting recipe of the week. %s\n", err.Error()),
})
return
}
if bytes, err := ctx.Cookie("search-filters"); err != nil {
fmt.Printf("ERROR: Failed to get search-filter cookie. %s\n", err.Error())
page = templates.HomePage(true, viewedRecipes, madeRecipes, recipeOfTheWeek, nil)
} 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 = templates.HomePage(true, viewedRecipes, madeRecipes, recipeOfTheWeek, nil)
} else {
page = templates.HomePage(true, viewedRecipes, madeRecipes, recipeOfTheWeek, &filters)
}
}
} else {
// Get the recipe of the week
recipeOfTheWeek, err := deps.RecipeService.GetRecipeOfTheWeek(nil)
if err != nil {
components.RenderErrorBanner(ctx, fmt.Sprintf("Error getting recipe of the week. %s\n", err.Error()))
ctx.JSON(http.StatusInternalServerError, gin.H{
"status": http.StatusInternalServerError,
"message": fmt.Sprintf("Error getting recipe of the week. %s\n", err.Error()),
})
return
}
if bytes, err := ctx.Cookie("search-filters"); err != nil {
fmt.Printf("ERROR: Failed to get search-filter cookie. %s\n", err.Error())
page = templates.HomePage(false, nil, nil, recipeOfTheWeek, nil)
} 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 = templates.HomePage(false, nil, nil, recipeOfTheWeek, nil)
} else {
page = templates.HomePage(false, nil, nil, recipeOfTheWeek, &filters)
}
}
}
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(nil)
} 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(nil)
} else {
page = pages.FavoritesPage(&filters)
}
}
ctx.HTML(http.StatusOK, "", layouts.AppLayout(title, page))
}
func CreatePage(ctx *gin.Context) {
deps := ctx.MustGet("deps").(*domainServer.InjectedDependencies)
// If not logged in, direct to the login page
if !domainServer.IsLoggedIn(ctx) {
ctx.Redirect(http.StatusSeeOther, domainServer.WEB_LOGIN)
return
}
// Ensure user is logged in with a valid account
if user := deps.UserService.GetAuthenicatedUser(ctx); user == nil {
// Log (stale) user out
domain.SetCookie(ctx, "jwt_token", "", -1)
domain.SetCookie(ctx, "search-filters", "", -1)
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)
if user == nil {
// User is failing to be found, direct to the login page
ctx.Redirect(http.StatusSeeOther, domainServer.WEB_LOGIN)
return
}
recipes, err := deps.RecipeService.GetUserRecipes(user.Id)
if err != nil {
components.RenderErrorBanner(ctx, fmt.Sprintf("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
}
favorites, err := deps.RecipeService.GetUserFavoriteRecipes(user.Id)
if err != nil {
components.RenderErrorBanner(ctx, fmt.Sprintf("Error getting favorite recipes. %s\n", err.Error()))
ctx.JSON(http.StatusInternalServerError, gin.H{
"status": http.StatusInternalServerError,
"message": fmt.Sprintf("Error getting favorite 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 {
components.RenderErrorBanner(ctx, fmt.Sprintf("Error getting user engagements. %s\n", err.Error()))
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))
}
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 {
components.RenderErrorBanner(ctx, fmt.Sprintf("ERROR: %s", err.Error()))
ctx.JSON(400, err.Error())
return
}
// Get signed in user, if they exist
var userId *int = nil
var loggedIn = domainServer.IsLoggedIn(ctx)
// Ensure user is logged in with a valid account
if user := deps.UserService.GetAuthenicatedUser(ctx); user == nil {
// Log (stale) user out
domain.SetCookie(ctx, "jwt_token", "", -1)
domain.SetCookie(ctx, "search-filters", "", -1)
loggedIn = false
}
if loggedIn {
storeId := ctx.MustGet("userId").(int)
userId = &storeId
}
// Get recipe
recipe, err := deps.RecipeService.GetRecipe(parsed, userId)
if err != nil {
components.RenderErrorBanner(ctx, fmt.Sprintf("ERROR: %s", err.Error()))
ctx.JSON(400, err.Error())
return
}
// Get user (owner)
user, err := deps.UserService.GetUser(recipe.UserId)
if err != nil {
components.RenderErrorBanner(ctx, fmt.Sprintf("ERROR: %s", err.Error()))
ctx.JSON(400, err.Error())
return
}
title := "Potion - View Recipe"
page := pages.RecipePage(*recipe, *user, loggedIn, deps.EnvironmentConfig.Domain)
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(nil, 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(nil, 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))
}