Compare commits

..

No commits in common. "0089e39a6b5b9bf2889239ada00bf55a529dab8c" and "45a6a23adb97740b9a64ae7ef707c8712915a56e" have entirely different histories.

13 changed files with 170 additions and 261 deletions

View File

@ -5,7 +5,7 @@ import "github.com/haydenhargreaves/Potion/internal/app/server"
const PORT = 3000 const PORT = 3000
func main() { func main() {
s := server.Init(PORT).Setup() s := server.Init(PORT).ConfigureAuth().ConnectDatabase().Setup()
defer s.DB.Close() defer s.DB.Close()
s.Start() s.Start()

View File

@ -61,7 +61,5 @@ func GoogleCallback(ctx *gin.Context) {
func Logout(ctx *gin.Context) { func Logout(ctx *gin.Context) {
// TODO: Use same values as the GoogleCallback function // TODO: Use same values as the GoogleCallback function
ctx.SetCookie("jwt_token", "", -1, "/", "", false, true) // TODO: Update settings ctx.SetCookie("jwt_token", "", -1, "/", "", false, true) // TODO: Update settings
ctx.SetCookie("search-filters", "", -1, "/", "", false, true)
ctx.Redirect(http.StatusSeeOther, domain.WEB_HOME) ctx.Redirect(http.StatusSeeOther, domain.WEB_HOME)
} }

View File

@ -82,6 +82,19 @@ func FavoritesPage(ctx *gin.Context) {
} }
} }
// 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)) ctx.HTML(http.StatusOK, "", layouts.AppLayout(title, page))
} }
@ -187,8 +200,24 @@ func RecipePage(ctx *gin.Context) {
return 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" title := "Potion - View Recipe"
page := pages.RecipePage(*recipe, *user, loggedIn, deps.EnvironmentConfig.Domain) page := pages.RecipePage(*recipe, *user, loggedIn)
ctx.HTML(http.StatusOK, "", layouts.AppLayout(title, page)) ctx.HTML(http.StatusOK, "", layouts.AppLayout(title, page))
} }

View File

@ -121,10 +121,6 @@ func SearchRecipesFavorites(ctx *gin.Context) {
// TODO: Error here if they're not logged in? // TODO: Error here if they're not logged in?
// Get user data (they should be logged in) // Get user data (they should be logged in)
if !domain.IsLoggedIn(ctx) {
ctx.JSON(http.StatusOK, gin.H{"error": "User is not logged in. User will be nil."})
}
userId := ctx.MustGet("userId").(int) userId := ctx.MustGet("userId").(int)
recipes, err := deps.RecipeService.SearchRecipes(filters, &userId, true) recipes, err := deps.RecipeService.SearchRecipes(filters, &userId, true)

View File

@ -2,9 +2,6 @@ package server
import ( import (
"fmt" "fmt"
"log"
"net/http"
"runtime/debug"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt/v5" "github.com/golang-jwt/jwt/v5"
@ -74,45 +71,3 @@ func JwtAuthMiddleWare(jwtSecretKey []byte) gin.HandlerFunc {
ctx.Next() ctx.Next()
} }
} }
func RecoveryMiddleware() gin.HandlerFunc {
return func(ctx *gin.Context) {
defer func() {
if r := recover(); r != nil {
// Log the panic with stack trace
err := fmt.Errorf("panic recovered: %v\n%s", r, debug.Stack())
log.Printf("[PANIC RECOVERY] %s", err)
ctx.JSON(http.StatusOK, gin.H{
"status": http.StatusOK,
"error": "API_PANIC_RECOVERED",
"message": err.Error(),
})
// Determine the content type of the request for appropriate response
// acceptHeader := ctx.Request.Header.Get("Accept")
//
// // Customize the response based on the request type (e.g., HTML vs. JSON)
// if strings.Contains(acceptHeader, "text/html") {
// // For browser requests (HTML), redirect to an error page or render a template
// ctx.HTML(http.StatusInternalServerError, "error.html", gin.H{
// "title": "Something went wrong",
// "message": "An unexpected error occurred. Please try again later.",
// })
// } else if strings.Contains(acceptHeader, "application/json") || ctx.Request.Method == http.MethodPost {
// // For API requests (JSON), return a JSON error response
// ctx.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{
// "error": "An internal server error occurred.",
// "message": "We're working to fix the problem. Please try again later.",
// // You might include a unique error ID here for tracking
// })
// } else {
// // Fallback for other content types
// ctx.AbortWithStatus(http.StatusInternalServerError)
// }
}
}()
ctx.Next()
}
}

View File

@ -4,6 +4,7 @@ import (
"database/sql" "database/sql"
"fmt" "fmt"
"net/http" "net/http"
"os"
"strings" "strings"
"github.com/a-h/templ/examples/integration-gin/gintemplrenderer" "github.com/a-h/templ/examples/integration-gin/gintemplrenderer"
@ -14,6 +15,7 @@ import (
domain "github.com/haydenhargreaves/Potion/internal/domain/server" domain "github.com/haydenhargreaves/Potion/internal/domain/server"
"github.com/haydenhargreaves/Potion/internal/infrastructure/auth" "github.com/haydenhargreaves/Potion/internal/infrastructure/auth"
"github.com/haydenhargreaves/Potion/internal/infrastructure/database/repository" "github.com/haydenhargreaves/Potion/internal/infrastructure/database/repository"
"github.com/joho/godotenv"
_ "github.com/lib/pq" _ "github.com/lib/pq"
) )
@ -51,26 +53,18 @@ func Init(port int) *Server {
return server return server
} }
// Start starts the server on the port provided when the server was initialized func (s *Server) ConfigureAuth() *Server {
func (s *Server) Start() { err := godotenv.Load(".env")
s.Router.Run(fmt.Sprintf(":%d", s.port))
}
func (s *Server) Setup() *Server {
// SETUP THE ENVIRONMENT CONFIGURATION
cfg, err := domain.LoadEnvironment()
if err != nil { if err != nil {
panic(err.Error()) fmt.Printf("No .env file found or error loading .env: %v. Relying on system environment variables.", err)
}
if cfg == nil {
panic("Environment configuration is nil, crashing.")
} }
// SETUP GOOGLE AUTH redirect_domain := os.Getenv("DOMAIN")
var ( var (
redirectUrl string = fmt.Sprintf("%s%s", cfg.Domain, domain.API_AUTH_CALLBACK) redirectUrl string = fmt.Sprintf("%s%s", redirect_domain, domain.API_AUTH_CALLBACK)
clientId string = cfg.GoogleClientId clientId string = os.Getenv("GOOGLE_CLIENT_ID")
clientSecret string = cfg.GoogleClientSecret clientSecret string = os.Getenv("GOOGLE_CLIENT_SECRET")
scope []string = []string{ scope []string = []string{
"https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/userinfo.email",
"https://www.googleapis.com/auth/userinfo.profile", "https://www.googleapis.com/auth/userinfo.profile",
@ -80,8 +74,18 @@ func (s *Server) Setup() *Server {
// Setup Google OAuth // Setup Google OAuth
auth.NewGoogleConfig(redirectUrl, clientId, clientSecret, scope) auth.NewGoogleConfig(redirectUrl, clientId, clientSecret, scope)
// SETUP DATABASE return s
db, err := sql.Open("postgres", cfg.DatabaseUrl) }
func (s *Server) ConnectDatabase() *Server {
err := godotenv.Load(".env")
if err != nil {
fmt.Printf("No .env file found or error loading .env: %v. Relying on system environment variables.", err)
}
var connUrl string = os.Getenv("DATABASE_URL")
db, err := sql.Open("postgres", connUrl)
if err != nil { if err != nil {
panic("Could not connect to database: " + err.Error()) panic("Could not connect to database: " + err.Error())
} }
@ -92,8 +96,21 @@ func (s *Server) Setup() *Server {
s.DB = db s.DB = db
// SETUP JWT return s
jwtSecret := []byte(cfg.JwtSecret) }
// Start starts the server on the port provided when the server was initialized
func (s *Server) Start() {
s.Router.Run(fmt.Sprintf(":%d", s.port))
}
func (s *Server) Setup() *Server {
err := godotenv.Load(".env")
if err != nil {
fmt.Printf("No .env file found or error loading .env: %v. Relying on system environment variables.", err)
}
jwtSecret := []byte(os.Getenv("JWT_SECRET"))
// Initialize and inject dependencies // Initialize and inject dependencies
userRepo := repository.NewUserRepository(s.DB) userRepo := repository.NewUserRepository(s.DB)
@ -109,11 +126,9 @@ func (s *Server) Setup() *Server {
AuthService: authService, AuthService: authService,
RecipeService: recipeService, RecipeService: recipeService,
EngagementService: engagementService, EngagementService: engagementService,
EnvironmentConfig: *cfg,
} }
// Apply middleware // Apply middleware
s.Router.Use(RecoveryMiddleware())
s.Router.Use(DepedencyInjectionMiddleware(deps)) s.Router.Use(DepedencyInjectionMiddleware(deps))
s.Router.Use(JwtAuthMiddleWare(jwtSecret)) s.Router.Use(JwtAuthMiddleWare(jwtSecret))
@ -134,8 +149,15 @@ func (s *Server) Setup() *Server {
// API router endpoints // API router endpoints
router_api.GET("/", func(ctx *gin.Context) { ctx.JSON(200, gin.H{"message": "Server is active."}) }) router_api.GET("/", func(ctx *gin.Context) { ctx.JSON(200, gin.H{"message": "Server is active."}) })
router_api.GET("/tmp", func(ctx *gin.Context) { router_api.GET("/tmp", func(ctx *gin.Context) {
deps := ctx.MustGet("deps").(*domain.InjectedDependencies) if !domain.IsLoggedIn(ctx) {
ctx.JSON(200, gin.H{"config": deps.EnvironmentConfig}) ctx.JSON(200, gin.H{"error": "User is not logged in. Please login to continue"})
return
}
userId := ctx.MustGet("userId").(int)
userEmail := ctx.MustGet("userEmail").(string)
ctx.JSON(200, gin.H{"id": userId, "email": userEmail})
}) })
// WEB router endpoints // WEB router endpoints
@ -166,6 +188,22 @@ func (s *Server) Setup() *Server {
router_api.GET("/user/recipes", handlers.GetUserRecipes) router_api.GET("/user/recipes", handlers.GetUserRecipes)
router_api.GET("/user/favorites", handlers.GetUserFavoriteRecipes) router_api.GET("/user/favorites", handlers.GetUserFavoriteRecipes)
router_api.GET("/user/temp", func(ctx *gin.Context) {
recipes, err := recipeService.GetUserMadeRecipes(3, 6)
if err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{
"recipes": recipes,
"error": err.Error(),
})
} else {
ctx.JSON(http.StatusBadRequest, gin.H{
"recipes": recipes,
"error": "",
})
}
})
// Engagement endpoints // Engagement endpoints
router_api.POST("/engagement/view/:id", handlers.EngagementViewRecipe) router_api.POST("/engagement/view/:id", handlers.EngagementViewRecipe)
router_api.POST("/engagement/share/:id", handlers.EngagementShareRecipe) router_api.POST("/engagement/share/:id", handlers.EngagementShareRecipe)
@ -179,7 +217,7 @@ func (s *Server) Setup() *Server {
// TODO: Use constants for errors? // TODO: Use constants for errors?
if strings.HasPrefix(path, domain.VERSION+domain.API) { if strings.HasPrefix(path, domain.VERSION+domain.API) {
ctx.JSON(http.StatusNotFound, gin.H{ ctx.JSON(http.StatusNotFound, gin.H{
"status": http.StatusNotFound, "status": 404,
"error": "API_NOT_FOUND", "error": "API_NOT_FOUND",
"message": "The request endpoint does not exist.", "message": "The request endpoint does not exist.",
"path": path, "path": path,

View File

@ -1,30 +1,14 @@
package domain package domain
import ( import (
"fmt"
"os"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt/v5" "github.com/golang-jwt/jwt/v5"
domainAuth "github.com/haydenhargreaves/Potion/internal/domain/auth" domainAuth "github.com/haydenhargreaves/Potion/internal/domain/auth"
domainEngagement "github.com/haydenhargreaves/Potion/internal/domain/engagement" domainEngagement "github.com/haydenhargreaves/Potion/internal/domain/engagement"
domainRecipe "github.com/haydenhargreaves/Potion/internal/domain/recipe" domainRecipe "github.com/haydenhargreaves/Potion/internal/domain/recipe"
domainUser "github.com/haydenhargreaves/Potion/internal/domain/user" domainUser "github.com/haydenhargreaves/Potion/internal/domain/user"
"github.com/joho/godotenv"
) )
// EnvironmentConfig stores the configuration of the environment. Anything loaded from the .env
// or docker environment will be stored here and can be accessed from the InjectedDependencies
// struct, which this is attached to.
type EnvironmentConfig struct {
GoogleClientId string
GoogleClientSecret string
JwtSecret string
DatabaseUrl string
Environment string
Domain string
}
// InjectedDependencies is a collection of dependencies that are injected into the application. They // InjectedDependencies is a collection of dependencies that are injected into the application. They
// are stored in the context and can be accessed by handlers via the context. // are stored in the context and can be accessed by handlers via the context.
type InjectedDependencies struct { type InjectedDependencies struct {
@ -32,7 +16,6 @@ type InjectedDependencies struct {
AuthService domainAuth.AuthService AuthService domainAuth.AuthService
RecipeService domainRecipe.RecipeService RecipeService domainRecipe.RecipeService
EngagementService domainEngagement.EngagementService EngagementService domainEngagement.EngagementService
EnvironmentConfig EnvironmentConfig
} }
// JwtClaims is the data stored in the JSON web token. All that is needed is the users ID and their // JwtClaims is the data stored in the JSON web token. All that is needed is the users ID and their
@ -49,71 +32,3 @@ func IsLoggedIn(ctx *gin.Context) bool {
_, email := ctx.Get("userEmail") _, email := ctx.Get("userEmail")
return id && email return id && email
} }
func LoadEnvironment() (*EnvironmentConfig, error) {
err := godotenv.Load(".env")
if err != nil {
fmt.Printf("No .env file found or error loading .env: %v. Relying on system environment variables.", err)
}
env := os.Getenv("ENVIRONMENT")
if env == "" {
return nil, fmt.Errorf("ENVIRONMENT environment variable is required.")
}
googleClientId := os.Getenv("GOOGLE_CLIENT_ID")
if googleClientId == "" {
return nil, fmt.Errorf("GOOGLE_CLIENT_ID environment variable is required.")
}
googleClientSecret := os.Getenv("GOOGLE_CLIENT_SECRET")
if googleClientSecret == "" {
return nil, fmt.Errorf("GOOGLE_CLIENT_SECRET environment variable is required.")
}
jwtSecret := os.Getenv("JWT_SECRET")
if jwtSecret == "" {
return nil, fmt.Errorf("JWT_SECRET environment variable is required.")
}
var domain string
if env == "dev" {
domain = os.Getenv("DOMAIN_DEV")
if domain == "" {
return nil, fmt.Errorf("DOMAIN_DEV environment variable is required when ENVIRONMENT is 'dev'.")
}
} else if env == "prod" {
domain = os.Getenv("DOMAIN_PROD")
if domain == "" {
return nil, fmt.Errorf("DOMAIN_PROD environment variable is required when ENVIRONMENT is 'prod'.")
}
} else {
return nil, fmt.Errorf("ENVIRONMENT environment variable is required and must be 'dev' or 'prod'.")
}
var dbUrl string
if env == "dev" {
dbUrl = os.Getenv("DATABASE_URL_DEV")
if dbUrl == "" {
return nil, fmt.Errorf("DATABASE_URL_DEV environment variable is required when ENVIRONMENT is 'dev'.")
}
} else if env == "prod" {
dbUrl = os.Getenv("DATABASE_URL_PROD")
if dbUrl == "" {
return nil, fmt.Errorf("DATABASE_URL_PROD environment variable is required when ENVIRONMENT is 'prod'.")
}
} else {
return nil, fmt.Errorf("ENVIRONMENT environment variable is required and must be 'dev' or 'prod'.")
}
cfg := &EnvironmentConfig{
GoogleClientId: googleClientId,
GoogleClientSecret: googleClientSecret,
JwtSecret: jwtSecret,
DatabaseUrl: dbUrl,
Environment: env,
Domain: domain,
}
return cfg, nil
}

View File

@ -2,7 +2,6 @@ package repository
import ( import (
"database/sql" "database/sql"
"fmt"
domain "github.com/haydenhargreaves/Potion/internal/domain/user" domain "github.com/haydenhargreaves/Potion/internal/domain/user"
_ "github.com/lib/pq" _ "github.com/lib/pq"
@ -36,10 +35,6 @@ func (r *UserRepository) CreateGoogleUser(googleUserInfo *domain.GoogleUserInfo,
return domain.User{}, err return domain.User{}, err
} }
if googleUserInfo == nil {
return domain.User{}, fmt.Errorf("Google user info provided was nil")
}
var user domain.User var user domain.User
query := `INSERT INTO users query := `INSERT INTO users
(GoogleId, Name, Email, ImageUrl, GoogleRefreshToken) (GoogleId, Name, Email, ImageUrl, GoogleRefreshToken)

View File

@ -211,7 +211,7 @@ func Navbar(current string) templ.Component {
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "\"><p class=\"select-none\">Potion</p></a></div><div class=\"hidden md:flex lg:flex items-center gap-8 select-none\">") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "\"><p class=\"select-none text-red-800\">Potion</p></a></div><div class=\"hidden md:flex lg:flex items-center gap-8 select-none\">")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }

View File

@ -6,7 +6,7 @@ import "github.com/haydenhargreaves/Potion/internal/domain/recipe"
import domainServer "github.com/haydenhargreaves/Potion/internal/domain/server" import domainServer "github.com/haydenhargreaves/Potion/internal/domain/server"
templ FavoriteList(recipes []domain.Recipe) { templ FavoriteList(recipes []domain.Recipe) {
<div id="result-list" class="flex flex-col w-full p-4 items-center"> <div id="result-list" class="flex flex-col w-full p-4 items-center">
for _, recipe := range recipes { for _, recipe := range recipes {
@favoriteResult(recipe) @favoriteResult(recipe)
} }
@ -15,17 +15,13 @@ templ FavoriteList(recipes []domain.Recipe) {
} else { } else {
<p class="text-gray-700 text-sm py-4">End of results</p> <p class="text-gray-700 text-sm py-4">End of results</p>
} }
</div> </div>
} }
templ favoriteResult(recipe domain.Recipe) { templ favoriteResult(recipe domain.Recipe) {
<div <div hx-post={ fmt.Sprintf(domainServer.API_ENGAGEMENT_VIEW, recipe.Id) } hx-trigger="click" hx-swap="none"
hx-post={ fmt.Sprintf(domainServer.API_ENGAGEMENT_VIEW, recipe.Id) } 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">
hx-trigger="click" <img class="bg-gray-50 size-56 md:size-40 rounded-md border-0" src="" />
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="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 md:flex-row items-center md:items-start justify-between w-full">
<div class="flex flex-col items-center md:items-start"> <div class="flex flex-col items-center md:items-start">
@ -55,24 +51,23 @@ templ favoriteResult(recipe domain.Recipe) {
<svg class="h-6 text-red-500" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg class="h-6 text-red-500" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path <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" 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" fill="currentColor"></path>
></path>
</svg> </svg>
</div> </div>
</div> </div>
<p class="text-sm py-2 text-center md:text-left">{ recipe.Description }</p> <p class="text-sm py-2 text-center md:text-left">{ recipe.Description }</p>
</div> </div>
</div> </div>
} }
templ FavoritesPage(filters domain.SearchFilters) { templ FavoritesPage(filters domain.SearchFilters) {
@components.Navbar("favorites") @components.Navbar("favorites")
<div class="w-full flex justify-center"> <div class="w-full flex justify-center">
<div class="mx-2 md:mx-0 w-full md:w-1/2 md:pt-14 min-h-screen border-l border-r border-gray-300 bg-white"> <div class="mx-2 md:mx-0 w-full md:w-1/2 md:pt-14 min-h-screen border-l border-r border-gray-300 bg-white">
@components.BannerText("Favorites") @components.BannerText("Favorites")
@components.SearchBar(filters, false, true, true) @components.SearchBar(filters, false, true, true)
<hr class="text-gray-300 w-full"/> <hr class="text-gray-300 w-full" />
@FavoriteList(nil) @FavoriteList(nil)
</div> </div>
</div> </div>
} }

View File

@ -91,7 +91,7 @@ func favoriteResult(recipe domain.Recipe) templ.Component {
var templ_7745c5c3_Var3 string var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf(domainServer.API_ENGAGEMENT_VIEW, recipe.Id)) templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf(domainServer.API_ENGAGEMENT_VIEW, recipe.Id))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/favorites.templ`, Line: 23, Col: 68} return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/favorites.templ`, Line: 22, Col: 71}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
@ -104,7 +104,7 @@ func favoriteResult(recipe domain.Recipe) templ.Component {
var templ_7745c5c3_Var4 string var templ_7745c5c3_Var4 string
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(recipe.Title) templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(recipe.Title)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/favorites.templ`, Line: 33, Col: 20} return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/favorites.templ`, Line: 29, Col: 24}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
@ -117,7 +117,7 @@ func favoriteResult(recipe domain.Recipe) templ.Component {
var templ_7745c5c3_Var5 string var templ_7745c5c3_Var5 string
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(recipe.Category) templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(recipe.Category)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/favorites.templ`, Line: 33, Col: 91} return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/favorites.templ`, Line: 29, Col: 95}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
@ -134,7 +134,7 @@ func favoriteResult(recipe domain.Recipe) templ.Component {
var templ_7745c5c3_Var6 string var templ_7745c5c3_Var6 string
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(recipe.Duration.Total) templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(recipe.Duration.Total)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/favorites.templ`, Line: 38, Col: 30} return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/favorites.templ`, Line: 34, Col: 35}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
@ -171,7 +171,7 @@ func favoriteResult(recipe domain.Recipe) templ.Component {
var templ_7745c5c3_Var7 string var templ_7745c5c3_Var7 string
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(recipe.Serves) templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(recipe.Serves)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/favorites.templ`, Line: 50, Col: 29} return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/favorites.templ`, Line: 46, Col: 34}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
@ -184,7 +184,7 @@ func favoriteResult(recipe domain.Recipe) templ.Component {
var templ_7745c5c3_Var8 string var templ_7745c5c3_Var8 string
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(recipe.Description) templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(recipe.Description)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/favorites.templ`, Line: 63, Col: 72} return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/pages/favorites.templ`, Line: 58, Col: 73}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {

View File

@ -291,7 +291,7 @@ templ buttonSection(favorited bool, id int, loggedIn bool) {
</section> </section>
} }
templ RecipePage(recipe domain.Recipe, user domainUser.User, loggedIn bool, domain string) { templ RecipePage(recipe domain.Recipe, user domainUser.User, loggedIn bool) {
@components.Navbar("") @components.Navbar("")
<div class="w-full flex justify-center"> <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"> <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">
@ -312,10 +312,10 @@ templ RecipePage(recipe domain.Recipe, user domainUser.User, loggedIn bool, doma
@tagList(recipe.Tags, recipe.Created, recipe.Modified) @tagList(recipe.Tags, recipe.Created, recipe.Modified)
</div> </div>
</div> </div>
@scripts(recipe.Id, domain) @scripts(recipe.Id)
} }
templ scripts(id int, domain string) { templ scripts(id int) {
<script> <script>
function shareButtonHandler() { function shareButtonHandler() {
const button = document.getElementById("share-button"); const button = document.getElementById("share-button");
@ -323,7 +323,7 @@ templ scripts(id int, domain string) {
if (navigator.clipboard && navigator.clipboard.writeText) { if (navigator.clipboard && navigator.clipboard.writeText) {
// TODO: Fix this to use the real domain somehow // TODO: Fix this to use the real domain somehow
const url = "{{ domain }}/v1/web/recipe/{{ id }}" const url = "http://localhost:7331/v1/web/recipe/{{ id }}"
navigator.clipboard.writeText(url).then(() => { navigator.clipboard.writeText(url).then(() => {
button.outerHTML = ` button.outerHTML = `
<button id="share-button" <button id="share-button"

File diff suppressed because one or more lines are too long