Compare commits
No commits in common. "5577d96b0d758cba16a411825ba83a26585b7048" and "d3d9c8a9e2ae966a3033313ac1d335093187accb" have entirely different histories.
5577d96b0d
...
d3d9c8a9e2
@ -262,6 +262,13 @@ found in **OTHER** section.
|
||||
- [x] GoogleRefreshToken () text
|
||||
- [x] Created (Required) date/time stamp
|
||||
|
||||
- [ ] Sessions: Represents a single user-session.
|
||||
- [ ] ID (PK) Serial
|
||||
- [ ] UserId (FK: User.Id, Required) Serial
|
||||
- [ ] Token (Required) text
|
||||
- [ ] Expiration (Required) date/time stamp
|
||||
- [ ] Created (Required) date/time stamp
|
||||
|
||||
- [ ] Engagements: Represents a single engagement from a single user.
|
||||
- [ ] ID (PK) Serial
|
||||
- [ ] Message () text (Used to store any relevant notes, if needed)
|
||||
|
||||
1
go.mod
1
go.mod
@ -22,7 +22,6 @@ require (
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.26.0 // indirect
|
||||
github.com/goccy/go-json v0.10.5 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
|
||||
github.com/gorilla/context v1.1.2 // indirect
|
||||
github.com/gorilla/securecookie v1.1.2 // indirect
|
||||
github.com/gorilla/sessions v1.4.0 // indirect
|
||||
|
||||
@ -2,14 +2,11 @@ package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
domain "github.com/haydenhargreaves/Potion/internal/domain/server"
|
||||
)
|
||||
|
||||
// GoogleLogin directs the user to Googles select user login page. Once the user has selected an
|
||||
// account, they will be directed to the GoogleCallback handler where the main logic resides.
|
||||
func GoogleLogin(ctx *gin.Context) {
|
||||
deps := ctx.MustGet("deps").(*domain.InjectedDependencies)
|
||||
url := deps.AuthService.GetGoogleAuthUrl()
|
||||
@ -17,13 +14,6 @@ func GoogleLogin(ctx *gin.Context) {
|
||||
ctx.Redirect(http.StatusSeeOther, url)
|
||||
}
|
||||
|
||||
// GoogleCallback is the callback handler when the user successfully logs in with their Google
|
||||
// account. They will be directed here and a JWT is generated. This JWT is stored in the users
|
||||
// cookies and will be used by protected routes to validate their login status.
|
||||
//
|
||||
// TODO: This route does not do the proper handling, need to work on the redirection or handling.
|
||||
//
|
||||
// We do not need to return all of this data, it is just for testing.
|
||||
func GoogleCallback(ctx *gin.Context) {
|
||||
deps := ctx.MustGet("deps").(*domain.InjectedDependencies)
|
||||
|
||||
@ -32,27 +22,9 @@ func GoogleCallback(ctx *gin.Context) {
|
||||
code string = ctx.Query("code")
|
||||
)
|
||||
|
||||
// TODO: Do something real, not just return data
|
||||
if jwt, dbUser, googleUserInfo, err := deps.AuthService.GoogleAuthSuccess(state, code); err != nil {
|
||||
if dbUser, googleUserInfo, err := deps.AuthService.GoogleAuthSuccess(state, code); err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
} else {
|
||||
// TODO: Update these values when using a real domain. Maybe an ENV?
|
||||
ctx.SetCookie(
|
||||
"jwt_token",
|
||||
jwt,
|
||||
int(time.Now().Add(7*24*time.Hour).Sub(time.Now()).Seconds()),
|
||||
"/",
|
||||
"localhost",
|
||||
false, // TODO: True in prod
|
||||
true,
|
||||
)
|
||||
ctx.JSON(http.StatusOK, gin.H{"jwt": jwt, "googleUserInfo": googleUserInfo, "dbUser": dbUser})
|
||||
ctx.JSON(http.StatusOK, gin.H{"googleUserInfo": googleUserInfo, "dbUser": dbUser})
|
||||
}
|
||||
}
|
||||
|
||||
// Logout removes the token from the user's browser. Effectively "logging them out." Routes that
|
||||
// require authentication will require the user to sign back in before accessing them again.
|
||||
func Logout(ctx *gin.Context) {
|
||||
// TODO: Use same values as the GoogleCallback function
|
||||
ctx.SetCookie("jwt_token", "", -1, "/", "localhost", false, true)
|
||||
}
|
||||
|
||||
@ -12,38 +12,3 @@ func LoginPage(ctx *gin.Context) {
|
||||
|
||||
ctx.HTML(200, "", layouts.AppLayout(title, page))
|
||||
}
|
||||
|
||||
func HomePage(ctx *gin.Context) {
|
||||
title := "Potion - Home"
|
||||
page := pages.HomePage()
|
||||
|
||||
ctx.HTML(200, "", layouts.AppLayout(title, page))
|
||||
}
|
||||
|
||||
func FavoritesPage(ctx *gin.Context) {
|
||||
title := "Potion - Favorites"
|
||||
page := pages.FavoritesPage()
|
||||
|
||||
ctx.HTML(200, "", layouts.AppLayout(title, page))
|
||||
}
|
||||
|
||||
func CreatePage(ctx *gin.Context) {
|
||||
title := "Potion - Create"
|
||||
page := pages.CreatePage()
|
||||
|
||||
ctx.HTML(200, "", layouts.AppLayout(title, page))
|
||||
}
|
||||
|
||||
func ProfilePage(ctx *gin.Context) {
|
||||
title := "Potion - Profile"
|
||||
page := pages.ProfilePage()
|
||||
|
||||
ctx.HTML(200, "", layouts.AppLayout(title, page))
|
||||
}
|
||||
|
||||
func ListPage(ctx *gin.Context) {
|
||||
title := "Potion - Shopping List"
|
||||
page := pages.ListPage()
|
||||
|
||||
ctx.HTML(200, "", layouts.AppLayout(title, page))
|
||||
}
|
||||
|
||||
@ -1,10 +1,7 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
domain "github.com/haydenhargreaves/Potion/internal/domain/server"
|
||||
)
|
||||
|
||||
@ -16,58 +13,3 @@ func DepedencyInjectionMiddleware(deps *domain.InjectedDependencies) gin.Handler
|
||||
ctx.Next()
|
||||
}
|
||||
}
|
||||
|
||||
// JwtAuthMiddleWare handles collection the JWT from the browser's cookies and setting the
|
||||
// appropriate data. If the data is not found, this middleware will do effectively nothing, by not
|
||||
// setting any values. Protected routes can use this lack of a value as a sign that the user is not
|
||||
// logged in and direct the user to login.
|
||||
func JwtAuthMiddleWare(jwtSecretKey []byte) gin.HandlerFunc {
|
||||
return func(ctx *gin.Context) {
|
||||
// JWT cookie not found
|
||||
tokenString, err := ctx.Cookie("jwt_token")
|
||||
if err != nil {
|
||||
ctx.Next()
|
||||
return
|
||||
}
|
||||
|
||||
claims := &domain.JwtClaims{}
|
||||
|
||||
token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {
|
||||
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
|
||||
}
|
||||
return jwtSecretKey, nil
|
||||
})
|
||||
|
||||
// Error occurred when parsing
|
||||
if err != nil {
|
||||
ctx.Next()
|
||||
return
|
||||
}
|
||||
|
||||
// NOTE: If we need deeper error handling
|
||||
// if err != nil {
|
||||
// if errors.Is(err, jwt.ErrSignatureInvalid) {
|
||||
// ctx.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token signature"})
|
||||
// } else if errors.Is(err, jwt.ErrTokenExpired) || errors.Is(err, jwt.ErrTokenNotValidYet) {
|
||||
// ctx.JSON(http.StatusUnauthorized, gin.H{"error": "Token expired or not yet valid"})
|
||||
// } else {
|
||||
// log.Printf("JWT parsing error: %v", err)
|
||||
// ctx.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token"})
|
||||
// }
|
||||
// ctx.Abort()
|
||||
// return
|
||||
// }
|
||||
|
||||
// Token is invalid
|
||||
if !token.Valid {
|
||||
ctx.Next()
|
||||
return
|
||||
}
|
||||
|
||||
// Found: Set the values
|
||||
ctx.Set("userId", claims.UserId)
|
||||
ctx.Set("userEmail", claims.Email)
|
||||
ctx.Next()
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,7 +3,6 @@ package server
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/a-h/templ/examples/integration-gin/gintemplrenderer"
|
||||
@ -102,26 +101,17 @@ func (s *Server) Start() {
|
||||
}
|
||||
|
||||
func (s *Server) Setup() *Server {
|
||||
err := godotenv.Load(".env")
|
||||
if err != nil {
|
||||
panic("Could not load env file")
|
||||
}
|
||||
|
||||
jwtSecret := []byte(os.Getenv("JWT_SECRET"))
|
||||
|
||||
// Initialize and inject dependencies
|
||||
userRepo := repository.NewUserRepository(s.DB)
|
||||
userService := service.NewUserService(userRepo)
|
||||
authService := service.NewAuthService(userRepo, jwtSecret)
|
||||
authService := service.NewAuthService(userRepo)
|
||||
|
||||
deps := &domain.InjectedDependencies{
|
||||
UserService: userService,
|
||||
AuthService: authService,
|
||||
}
|
||||
|
||||
// Apply middleware
|
||||
s.Router.Use(DepedencyInjectionMiddleware(deps))
|
||||
s.Router.Use(JwtAuthMiddleWare(jwtSecret))
|
||||
|
||||
// Wrap all routes with a version
|
||||
router_v1 := s.Router.Group("/v1")
|
||||
@ -135,31 +125,13 @@ func (s *Server) Setup() *Server {
|
||||
|
||||
// API router endpoints
|
||||
router_api.GET("/", func(ctx *gin.Context) { ctx.JSON(200, gin.H{"message": "Server is active."}) })
|
||||
router_api.GET("/tmp", func(ctx *gin.Context) {
|
||||
if !domain.IsLoggedIn(ctx) {
|
||||
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
|
||||
router_web.GET("/login", handlers.LoginPage)
|
||||
router_web.GET("/", func(ctx *gin.Context) { ctx.Redirect(http.StatusSeeOther, "/v1/web/home") })
|
||||
router_web.GET("/home", handlers.HomePage)
|
||||
router_web.GET("/favorites", handlers.FavoritesPage)
|
||||
router_web.GET("/create", handlers.CreatePage)
|
||||
router_web.GET("/profile", handlers.ProfilePage)
|
||||
router_web.GET("/list", handlers.ListPage)
|
||||
|
||||
// Authentication
|
||||
// Google oauth
|
||||
router_api.GET("/auth/login", handlers.GoogleLogin)
|
||||
router_api.GET("/auth/callback", handlers.GoogleCallback)
|
||||
router_api.GET("/auth/logout", handlers.Logout)
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
@ -3,11 +3,8 @@ package service
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
domainAuth "github.com/haydenhargreaves/Potion/internal/domain/auth"
|
||||
domainServer "github.com/haydenhargreaves/Potion/internal/domain/server"
|
||||
domain "github.com/haydenhargreaves/Potion/internal/domain/user"
|
||||
"github.com/haydenhargreaves/Potion/internal/infrastructure/auth"
|
||||
"golang.org/x/oauth2"
|
||||
@ -30,7 +27,6 @@ import (
|
||||
// AuthService implements the domain.AuthService defined in the domain module.
|
||||
type AuthService struct {
|
||||
userRepository domain.UserRepository
|
||||
jwtSecret []byte
|
||||
}
|
||||
|
||||
// Compile-time check to ensure the AuthService implements domain.AuthService
|
||||
@ -38,11 +34,8 @@ var _ domainAuth.AuthService = (*AuthService)(nil)
|
||||
|
||||
// NewAuthService creates a user service object which can be passed into the context. The service
|
||||
// requires a user repository which it will use to hit the database when needed.
|
||||
func NewAuthService(userRepository domain.UserRepository, jwtSecret []byte) domainAuth.AuthService {
|
||||
return &AuthService{
|
||||
userRepository: userRepository,
|
||||
jwtSecret: jwtSecret,
|
||||
}
|
||||
func NewAuthService(userRepository domain.UserRepository) domainAuth.AuthService {
|
||||
return &AuthService{userRepository: userRepository}
|
||||
}
|
||||
|
||||
// GetGoogleAuthUrl generates a URL which is used to redirect the user to the Google sign in page.
|
||||
@ -60,65 +53,42 @@ func (s *AuthService) GetGoogleAuthUrl() string {
|
||||
|
||||
// GoogleAuthSuccess accepts the data from the Google login endpoint and uses it to fetch the users
|
||||
// data. The data is then used to log the user in or create an account.
|
||||
func (s *AuthService) GoogleAuthSuccess(state, code string) (string, domain.User, domain.GoogleUserInfo, error) {
|
||||
func (s *AuthService) GoogleAuthSuccess(state, code string) (domain.User, domain.GoogleUserInfo, error) {
|
||||
// Ensure the state matches, prevents M.I.T.M. attacks
|
||||
if state != "randomstate" {
|
||||
return "", domain.User{}, domain.GoogleUserInfo{}, fmt.Errorf("States don't match, received %s", state)
|
||||
return domain.User{}, domain.GoogleUserInfo{}, fmt.Errorf("States don't match, received %s", state)
|
||||
}
|
||||
|
||||
// Get access token from Google
|
||||
token, err := auth.GoogleAuthConfig.Exchange(context.Background(), code)
|
||||
if err != nil {
|
||||
return "", domain.User{}, domain.GoogleUserInfo{}, fmt.Errorf("Code exchange failed: %s", err.Error())
|
||||
return domain.User{}, domain.GoogleUserInfo{}, fmt.Errorf("Code exchange failed: %s", err.Error())
|
||||
}
|
||||
|
||||
// Use the access token to get user data
|
||||
googleUserInfo, err := auth.GetUserData(token.AccessToken)
|
||||
if err != nil {
|
||||
return "", domain.User{}, domain.GoogleUserInfo{}, err
|
||||
return domain.User{}, domain.GoogleUserInfo{}, err
|
||||
}
|
||||
|
||||
// Attempt to get the user, user is nil when they don't exit
|
||||
user, err := s.userRepository.GetGoogleUser(googleUserInfo.Id)
|
||||
if err != nil {
|
||||
return "", domain.User{}, domain.GoogleUserInfo{}, fmt.Errorf("Failed to get db user: %s", err)
|
||||
return domain.User{}, domain.GoogleUserInfo{}, fmt.Errorf("Failed to get db user: %s", err)
|
||||
}
|
||||
|
||||
// A user was found
|
||||
if user != nil {
|
||||
jwt, err := generateJwt(user.Id, user.Email, s.jwtSecret)
|
||||
return jwt, *user, googleUserInfo, err
|
||||
return *user, googleUserInfo, nil
|
||||
}
|
||||
|
||||
// user did not exist, need to create one
|
||||
newUser, err := s.userRepository.CreateGoogleUser(&googleUserInfo, token.RefreshToken)
|
||||
if err != nil {
|
||||
return "", domain.User{}, domain.GoogleUserInfo{}, fmt.Errorf("Repository failed to create user: %s", err.Error())
|
||||
return domain.User{}, domain.GoogleUserInfo{}, fmt.Errorf("Repository failed to create user: %s", err.Error())
|
||||
}
|
||||
|
||||
jwt, err := generateJwt(newUser.Id, newUser.Email, s.jwtSecret)
|
||||
return jwt, newUser, googleUserInfo, err
|
||||
// temporary
|
||||
return newUser, googleUserInfo, nil
|
||||
|
||||
}
|
||||
|
||||
func generateJwt(userId int, email string, jwtSecret []byte) (string, error) {
|
||||
expiration := time.Now().Add(7 * 24 * time.Hour)
|
||||
|
||||
claims := &domainServer.JwtClaims{
|
||||
UserId: userId,
|
||||
Email: email,
|
||||
RegisteredClaims: jwt.RegisteredClaims{
|
||||
ExpiresAt: jwt.NewNumericDate(expiration),
|
||||
IssuedAt: jwt.NewNumericDate(time.Now()),
|
||||
Subject: fmt.Sprintf("%d", userId),
|
||||
},
|
||||
}
|
||||
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
tokenString, err := token.SignedString(jwtSecret)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Failed to sign jwt: %s", err.Error())
|
||||
}
|
||||
|
||||
return tokenString, nil
|
||||
}
|
||||
|
||||
@ -6,5 +6,5 @@ import (
|
||||
|
||||
type AuthService interface {
|
||||
GetGoogleAuthUrl() string
|
||||
GoogleAuthSuccess(state, code string) (string, domain.User, domain.GoogleUserInfo, error)
|
||||
GoogleAuthSuccess(state, code string) (domain.User, domain.GoogleUserInfo, error)
|
||||
}
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
package domain
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
domainAuth "github.com/haydenhargreaves/Potion/internal/domain/auth"
|
||||
domainUser "github.com/haydenhargreaves/Potion/internal/domain/user"
|
||||
)
|
||||
@ -11,16 +9,3 @@ type InjectedDependencies struct {
|
||||
UserService domainUser.UserService
|
||||
AuthService domainAuth.AuthService
|
||||
}
|
||||
|
||||
type JwtClaims struct {
|
||||
UserId int `json:"id"`
|
||||
Email string `json:"email"`
|
||||
jwt.RegisteredClaims
|
||||
}
|
||||
|
||||
// IsLoggedIn checks the cookies in a request and returns whether the user is logged in.
|
||||
func IsLoggedIn(ctx *gin.Context) bool {
|
||||
_, id := ctx.Get("userId")
|
||||
_, email := ctx.Get("userEmail")
|
||||
return id && email
|
||||
}
|
||||
|
||||
0
internal/templates/components/button.templ
Normal file
0
internal/templates/components/button.templ
Normal file
10
internal/templates/components/button_templ.go
Normal file
10
internal/templates/components/button_templ.go
Normal file
@ -0,0 +1,10 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.865
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import templruntime "github.com/a-h/templ/runtime"
|
||||
|
||||
var _ = templruntime.GeneratedTemplate
|
||||
@ -1,98 +0,0 @@
|
||||
package components
|
||||
|
||||
import "strings"
|
||||
|
||||
templ hamburgerMenu() {
|
||||
<script>
|
||||
function toggleMenu() {
|
||||
const menu = document.getElementById("mobile-menu-content");
|
||||
const carotButton = document.getElementById("mobile-menu-button-carot");
|
||||
const barsButton = document.getElementById("mobile-menu-button-bars");
|
||||
|
||||
if (menu.classList.contains("flex")) {
|
||||
menu.classList.remove("flex");
|
||||
menu.classList.add("hidden");
|
||||
|
||||
carotButton.classList.add("hidden");
|
||||
barsButton.classList.remove("hidden");
|
||||
} else {
|
||||
menu.classList.add("flex");
|
||||
menu.classList.remove("hidden");
|
||||
|
||||
carotButton.classList.remove("hidden");
|
||||
barsButton.classList.add("hidden");
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
id="mobile-menu-content"
|
||||
class="hidden w-full flex-col items-center absolute top-[100%] left-0 py-2 bg-white border-b border-gray-300 shadow-sm shadow-gray-300"
|
||||
>
|
||||
@dropdownLink("Home", "/v1/web/home")
|
||||
@dropdownLink("Favorites", "/v1/web/favorites")
|
||||
@dropdownLink("Create", "/v1/web/create")
|
||||
@dropdownLink("Profile", "/v1/web/profile")
|
||||
@dropdownLink("Shopping List", "/v1/web/list")
|
||||
</div>
|
||||
}
|
||||
|
||||
templ navLink(current, name, url string) {
|
||||
<a href={ templ.SafeURL(url) }
|
||||
if strings.ToLower(current)==strings.ToLower(name) {
|
||||
class="text-gray-700 border-b-2 border-blue-500 px-1 cursor-pointer"
|
||||
} else {
|
||||
class="text-gray-700 border-b-2 hover:border-blue-400 px-1 cursor-pointer border-white duration-150"
|
||||
}
|
||||
>
|
||||
{ name }
|
||||
</a>
|
||||
}
|
||||
|
||||
templ dropdownLink(name, url string) {
|
||||
<a class="py-2" href={ templ.SafeURL(url) }>
|
||||
{ name }
|
||||
</a>
|
||||
}
|
||||
|
||||
templ listIcon(current, name, url string) {
|
||||
<a href={ templ.SafeURL(url) }>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"
|
||||
if strings.ToLower(current)==strings.ToLower(name) {
|
||||
class="h-4 text-blue-500"
|
||||
} else {
|
||||
class="h-4 text-gray-700 hover:text-blue-400 duration-150"
|
||||
}
|
||||
>
|
||||
<path fill="currentColor" d="M0 24C0 10.7 10.7 0 24 0L69.5 0c22 0 41.5 12.8 50.6 32l411 0c26.3 0 45.5 25 38.6 50.4l-41 152.3c-8.5 31.4-37 53.3-69.5 53.3l-288.5 0 5.4 28.5c2.2 11.3 12.1 19.5 23.6 19.5L488 336c13.3 0 24 10.7 24 24s-10.7 24-24 24l-288.3 0c-34.6 0-64.3-24.6-70.7-58.5L77.4 54.5c-.7-3.8-4-6.5-7.9-6.5L24 48C10.7 48 0 37.3 0 24zM128 464a48 48 0 1 1 96 0 48 48 0 1 1 -96 0zm336-48a48 48 0 1 1 0 96 48 48 0 1 1 0-96z" />
|
||||
</svg>
|
||||
</a>
|
||||
}
|
||||
|
||||
templ Navbar(current string) {
|
||||
<nav class="relative w-full px-8 md:px-44 p-4 border-b border-gray-300 shadow-sm shadow-gray-300 bg-white flex justify-between items-center">
|
||||
<div>
|
||||
<p class="select-none">Potion</p>
|
||||
</div>
|
||||
<div class="hidden md:flex lg:flex items-center gap-8 select-none">
|
||||
@navLink(current, "Home", "/v1/web/home")
|
||||
@navLink(current, "Favorites", "/v1/web/favorites")
|
||||
@navLink(current, "Create", "/v1/web/create")
|
||||
@navLink(current, "Profile", "/v1/web/profile")
|
||||
@listIcon(current, "List", "/v1/web/list")
|
||||
</div>
|
||||
<div class="md:hidden grid place-content-center">
|
||||
<button onclick="toggleMenu()" class="p-2">
|
||||
// carot
|
||||
<svg id="mobile-menu-button-carot" class="hidden size-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512">
|
||||
<path d="M182.6 137.4c-12.5-12.5-32.8-12.5-45.3 0l-128 128c-9.2 9.2-11.9 22.9-6.9 34.9s16.6 19.8 29.6 19.8l256 0c12.9 0 24.6-7.8 29.6-19.8s2.2-25.7-6.9-34.9l-128-128z"/>
|
||||
</svg>
|
||||
// bars
|
||||
<svg id="mobile-menu-button-bars" class="size-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512">
|
||||
<path fill="currentColor" d="M0 96C0 78.3 14.3 64 32 64l384 0c17.7 0 32 14.3 32 32s-14.3 32-32 32L32 128C14.3 128 0 113.7 0 96zM0 256c0-17.7 14.3-32 32-32l384 0c17.7 0 32 14.3 32 32s-14.3 32-32 32L32 288c-17.7 0-32-14.3-32-32zM448 416c0 17.7-14.3 32-32 32L32 448c-17.7 0-32-14.3-32-32s14.3-32 32-32l384 0c17.7 0 32 14.3 32 32z" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
@hamburgerMenu()
|
||||
</nav>
|
||||
}
|
||||
@ -1,297 +0,0 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.865
|
||||
package components
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import templruntime "github.com/a-h/templ/runtime"
|
||||
|
||||
import "strings"
|
||||
|
||||
func hamburgerMenu() 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_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<script>\n function toggleMenu() {\n const menu = document.getElementById(\"mobile-menu-content\");\n const carotButton = document.getElementById(\"mobile-menu-button-carot\");\n const barsButton = document.getElementById(\"mobile-menu-button-bars\");\n\n if (menu.classList.contains(\"flex\")) {\n menu.classList.remove(\"flex\");\n menu.classList.add(\"hidden\");\n\n carotButton.classList.add(\"hidden\");\n barsButton.classList.remove(\"hidden\");\n } else {\n menu.classList.add(\"flex\");\n menu.classList.remove(\"hidden\");\n\n carotButton.classList.remove(\"hidden\");\n barsButton.classList.add(\"hidden\");\n }\n }\n </script><div id=\"mobile-menu-content\" class=\"hidden w-full flex-col items-center absolute top-[100%] left-0 py-2 bg-white border-b border-gray-300 shadow-sm shadow-gray-300\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = dropdownLink("Home", "/v1/web/home").Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = dropdownLink("Favorites", "/v1/web/favorites").Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = dropdownLink("Create", "/v1/web/create").Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = dropdownLink("Profile", "/v1/web/profile").Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = dropdownLink("Shopping List", "/v1/web/list").Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "</div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func navLink(current, name, url string) 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_Var2 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var2 == nil {
|
||||
templ_7745c5c3_Var2 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "<a href=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var3 templ.SafeURL = templ.SafeURL(url)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var3)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if strings.ToLower(current) == strings.ToLower(name) {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, " class=\"text-gray-700 border-b-2 border-blue-500 px-1 cursor-pointer\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
} else {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, " class=\"text-gray-700 border-b-2 hover:border-blue-400 px-1 cursor-pointer border-white duration-150\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, ">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var4 string
|
||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(name)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/components/navbar.templ`, Line: 48, Col: 8}
|
||||
}
|
||||
_, 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, 8, "</a>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func dropdownLink(name, url string) 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, 9, "<a class=\"py-2\" href=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var6 templ.SafeURL = templ.SafeURL(url)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var6)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var7 string
|
||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(name)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/components/navbar.templ`, Line: 54, Col: 8}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "</a>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func listIcon(current, name, url string) 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_Var8 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var8 == nil {
|
||||
templ_7745c5c3_Var8 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "<a href=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var9 templ.SafeURL = templ.SafeURL(url)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var9)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "\"><svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 576 512\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if strings.ToLower(current) == strings.ToLower(name) {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, " class=\"h-4 text-blue-500\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
} else {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, " class=\"h-4 text-gray-700 hover:text-blue-400 duration-150\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "><path fill=\"currentColor\" d=\"M0 24C0 10.7 10.7 0 24 0L69.5 0c22 0 41.5 12.8 50.6 32l411 0c26.3 0 45.5 25 38.6 50.4l-41 152.3c-8.5 31.4-37 53.3-69.5 53.3l-288.5 0 5.4 28.5c2.2 11.3 12.1 19.5 23.6 19.5L488 336c13.3 0 24 10.7 24 24s-10.7 24-24 24l-288.3 0c-34.6 0-64.3-24.6-70.7-58.5L77.4 54.5c-.7-3.8-4-6.5-7.9-6.5L24 48C10.7 48 0 37.3 0 24zM128 464a48 48 0 1 1 96 0 48 48 0 1 1 -96 0zm336-48a48 48 0 1 1 0 96 48 48 0 1 1 0-96z\"></path></svg></a>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func Navbar(current string) 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_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, 17, "<nav class=\"relative w-full px-8 md:px-44 p-4 border-b border-gray-300 shadow-sm shadow-gray-300 bg-white flex justify-between items-center\"><div><p class=\"select-none\">Potion</p></div><div class=\"hidden md:flex lg:flex items-center gap-8 select-none\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = navLink(current, "Home", "/v1/web/home").Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = navLink(current, "Favorites", "/v1/web/favorites").Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = navLink(current, "Create", "/v1/web/create").Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = navLink(current, "Profile", "/v1/web/profile").Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = listIcon(current, "List", "/v1/web/list").Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "</div><div class=\"md:hidden grid place-content-center\"><button onclick=\"toggleMenu()\" class=\"p-2\"><svg id=\"mobile-menu-button-carot\" class=\"hidden size-5\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"><path d=\"M182.6 137.4c-12.5-12.5-32.8-12.5-45.3 0l-128 128c-9.2 9.2-11.9 22.9-6.9 34.9s16.6 19.8 29.6 19.8l256 0c12.9 0 24.6-7.8 29.6-19.8s2.2-25.7-6.9-34.9l-128-128z\"></path></svg><svg id=\"mobile-menu-button-bars\" class=\"size-5\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"><path fill=\"currentColor\" d=\"M0 96C0 78.3 14.3 64 32 64l384 0c17.7 0 32 14.3 32 32s-14.3 32-32 32L32 128C14.3 128 0 113.7 0 96zM0 256c0-17.7 14.3-32 32-32l384 0c17.7 0 32 14.3 32 32s-14.3 32-32 32L32 288c-17.7 0-32-14.3-32-32zM448 416c0 17.7-14.3 32-32 32L32 448c-17.7 0-32-14.3-32-32s14.3-32 32-32l384 0c17.7 0 32 14.3 32 32z\"></path></svg></button></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = hamburgerMenu().Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "</nav>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
var _ = templruntime.GeneratedTemplate
|
||||
@ -3,17 +3,18 @@ package templates
|
||||
// AppLayout is the main application layout, this does not contain any content other than
|
||||
// meta data, links, scripts and whatever is passed into it as a component.
|
||||
templ AppLayout(title string, child templ.Component) {
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<title>{ title }</title>
|
||||
<link rel="stylesheet" href="/v1/web/static/css/tailwind.css"/>
|
||||
<script src="https://unpkg.com/htmx.org@2.0.4"></script>
|
||||
</head>
|
||||
<body class="bg-gray-100">
|
||||
@child
|
||||
</body>
|
||||
</html>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>{ title }</title>
|
||||
<link rel="stylesheet" href="/v1/web/static/css/tailwind.css" />
|
||||
<script src="https://unpkg.com/htmx.org@2.0.4"></script>
|
||||
</head>
|
||||
<body>
|
||||
@child
|
||||
</body>
|
||||
</html>
|
||||
}
|
||||
|
||||
@ -38,13 +38,13 @@ func AppLayout(title string, child templ.Component) templ.Component {
|
||||
var templ_7745c5c3_Var2 string
|
||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(title)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/layouts/app_layout.templ`, Line: 11, Col: 17}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/layouts/app_layout.templ`, Line: 12, Col: 16}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "</title><link rel=\"stylesheet\" href=\"/v1/web/static/css/tailwind.css\"><script src=\"https://unpkg.com/htmx.org@2.0.4\"></script></head><body class=\"bg-gray-100\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "</title><link rel=\"stylesheet\" href=\"/v1/web/static/css/tailwind.css\"><script src=\"https://unpkg.com/htmx.org@2.0.4\"></script></head><body>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
||||
@ -1,7 +0,0 @@
|
||||
package templates
|
||||
|
||||
import "github.com/haydenhargreaves/Potion/internal/templates/components"
|
||||
|
||||
templ CreatePage() {
|
||||
@components.Navbar("create")
|
||||
}
|
||||
@ -1,42 +0,0 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.865
|
||||
package templates
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import templruntime "github.com/a-h/templ/runtime"
|
||||
|
||||
import "github.com/haydenhargreaves/Potion/internal/templates/components"
|
||||
|
||||
func CreatePage() 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_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = components.Navbar("create").Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
var _ = templruntime.GeneratedTemplate
|
||||
@ -1,7 +0,0 @@
|
||||
package templates
|
||||
|
||||
import "github.com/haydenhargreaves/Potion/internal/templates/components"
|
||||
|
||||
templ FavoritesPage() {
|
||||
@components.Navbar("favorites")
|
||||
}
|
||||
@ -1,42 +0,0 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.865
|
||||
package templates
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import templruntime "github.com/a-h/templ/runtime"
|
||||
|
||||
import "github.com/haydenhargreaves/Potion/internal/templates/components"
|
||||
|
||||
func FavoritesPage() 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_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = components.Navbar("favorites").Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
var _ = templruntime.GeneratedTemplate
|
||||
@ -1,7 +0,0 @@
|
||||
package templates
|
||||
|
||||
import "github.com/haydenhargreaves/Potion/internal/templates/components"
|
||||
|
||||
templ HomePage() {
|
||||
@components.Navbar("home")
|
||||
}
|
||||
@ -1,42 +0,0 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.865
|
||||
package templates
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import templruntime "github.com/a-h/templ/runtime"
|
||||
|
||||
import "github.com/haydenhargreaves/Potion/internal/templates/components"
|
||||
|
||||
func HomePage() 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_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = components.Navbar("home").Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
var _ = templruntime.GeneratedTemplate
|
||||
@ -1,8 +0,0 @@
|
||||
package templates
|
||||
|
||||
import "github.com/haydenhargreaves/Potion/internal/templates/components"
|
||||
|
||||
templ ListPage() {
|
||||
@components.Navbar("list")
|
||||
}
|
||||
|
||||
@ -1,42 +0,0 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.865
|
||||
package templates
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import templruntime "github.com/a-h/templ/runtime"
|
||||
|
||||
import "github.com/haydenhargreaves/Potion/internal/templates/components"
|
||||
|
||||
func ListPage() 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_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = components.Navbar("list").Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
var _ = templruntime.GeneratedTemplate
|
||||
@ -1,9 +0,0 @@
|
||||
package templates
|
||||
|
||||
import "github.com/haydenhargreaves/Potion/internal/templates/components"
|
||||
|
||||
templ ProfilePage() {
|
||||
@components.Navbar("profile")
|
||||
}
|
||||
|
||||
|
||||
@ -1,42 +0,0 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.865
|
||||
package templates
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import templruntime "github.com/a-h/templ/runtime"
|
||||
|
||||
import "github.com/haydenhargreaves/Potion/internal/templates/components"
|
||||
|
||||
func ProfilePage() 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_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = 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
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
var _ = templruntime.GeneratedTemplate
|
||||
@ -1 +1,3 @@
|
||||
@import "tailwindcss";
|
||||
@source "./internal/templates/**/*.templ";
|
||||
@plugin "daisyui";
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user