add basic cart

This commit is contained in:
2025-03-03 21:09:47 +01:00
parent 1dc10d61bc
commit 5e22be5074
9 changed files with 373 additions and 4 deletions

View File

@@ -0,0 +1,239 @@
package controllers
import (
"fmt"
"net/http"
"strconv"
"crypto/rand"
"encoding/hex"
"github.com/gin-gonic/gin"
"example.com/gin/test/models"
//"example.com/gin/test/services"
"example.com/gin/test/repositories"
)
type CartItemController interface {
//CRUDController
CartItemView(*gin.Context)
AddItemHandler(*gin.Context)
//EditCartItemHandler(*gin.Context)
//DeleteCartItemHandler(*gin.Context)
}
type cartItemController struct {}
func NewCartItemController() CartItemController {
return &cartItemController{}
}
func generateSessionId() string {
bytes := make([]byte, 16) // 16 bytes = 128 bits
_, err := rand.Read(bytes)
if err != nil {
panic("failed to generate session ID")
}
return hex.EncodeToString(bytes)
}
func GetSessionId(ctx *gin.Context) string {
sessionId, err := ctx.Cookie("session_id")
if err != nil {
sessionId = generateSessionId()
ctx.SetCookie("session_id", sessionId, 3600, "/", "", false, true)
}
return sessionId
}
func (rc *cartItemController) NewCartItemFromForm(ctx *gin.Context) (models.CartItem, error) {
fmt.Println("NEWITEM")
sessionId := GetSessionId(ctx)
shopItemId, err := strconv.Atoi(ctx.PostForm("ShopItemId"))
if err != nil {
return models.CartItem{}, err
}
quantity := 1
//quantity, err := strconv.Atoi(ctx.PostForm("quantity"))
//if err != nil {
// return models.CartItem{}, err
//}
cartItem := models.CartItem{
SessionId: sessionId,
ShopItemId: shopItemId,
Quantity: quantity,
}
fmt.Println("NEWITEM")
return cartItem, nil
}
func (rc *cartItemController) Create(c *gin.Context) {
cartItem, err := rc.NewCartItemFromForm(c)
if err != nil {
ReplyError(c, fmt.Errorf("cartItem creation failed: %s", err))
return
}
_, err = repositories.CartItems.Create(cartItem)
if err != nil {
ReplyError(c, fmt.Errorf("cartItem creation failed: %s", err))
return
}
ReplyOK(c, "cartItem was created")
}
func (rc *cartItemController) Update(c *gin.Context) {
cartItem, err := rc.NewCartItemFromForm(c)
if err != nil {
ReplyError(c, err)
return
}
_, err = repositories.CartItems.Update(cartItem)
if err != nil {
ReplyError(c, fmt.Errorf("cartItem creation failed: %s", err))
return
}
ReplyOK(c, "cartItem was updated")
}
//func (rc *cartItemController) Delete(c *gin.Context) {
// err := repositories.CartItems.DeleteBySessionId(c.Param("id"))
//
// if err != nil {
// ReplyError(c, fmt.Errorf("cartItem deletion failed: %s", err))
// return
// }
//
// ReplyOK(c, "cartItem was deleted")
//}
func (rc *cartItemController) CartItemView(c *gin.Context) {
sessionId := GetSessionId(c)
cartItems, err := repositories.CartItems.GetAllBySession(sessionId)
if err != nil {
c.HTML(http.StatusBadRequest, "cart.html", gin.H{ "data": gin.H{ "error": err } })
}
data := CreateSessionData(c, gin.H{
"cartItems": cartItems,
})
c.HTML(http.StatusOK, "cart.html", data)
}
func (rc *cartItemController) AddItemHandler(c *gin.Context) {
cartItem, err := rc.NewCartItemFromForm(c)
if err != nil {
fmt.Println(err)
c.HTML(http.StatusBadRequest, "cart.html", gin.H{ "error": err })
return
}
_, err = repositories.CartItems.Create(cartItem)
if err != nil {
data := CreateSessionData(c, gin.H{
"error": err,
"success": "",
})
c.HTML(http.StatusOK, "cart.html", data)
return
}
rc.CartItemView(c)
}
//func (rc *cartItemController) EditItemHandler(c *gin.Context) {
// cartItem, err := rc.NewCartItemFromForm(c)
//
// if err != nil {
// c.HTML(http.StatusBadRequest, "edititem.html", gin.H{ "error": err })
// return
// }
//
// newCartItem, err := repositories.ShopItems.GetById(c.Param("id"))
//
// if err != nil {
// c.HTML(http.StatusBadRequest, "edititem.html", gin.H{ "error": err })
// return
// }
//
// newCartItem.Name = cartItem.Name
// newCartItem.Abstract = cartItem.Abstract
// newCartItem.Description = cartItem.Description
// newCartItem.Price = cartItem.Price
// newCartItem.IsPublic = cartItem.IsPublic
// newCartItem.Tags = cartItem.Tags
//
// tags, err := repositories.Tags.GetAll()
// if err != nil {
// c.HTML(http.StatusBadRequest, "edititem.html", gin.H{ "error": err })
// return
// }
//
// _, err = repositories.CartItems.Update(newShopItem)
// if err != nil {
// data := CreateSessionData(c, gin.H{
// "error": err,
// "success": "",
// "tags": tags,
// })
//
// c.HTML(http.StatusOK, "edititem.html", data)
// return
// }
//
// data := CreateSessionData(c, gin.H{
// "error": "",
// "success": fmt.Sprintf("Item '%s' Updated", newCartItem.Name),
// "tags": tags,
// })
//
// c.HTML(http.StatusOK, "edititem.html", data)
//}
//func (rc *cartItemController) DeleteItemHandler(c *gin.Context) {
// err := repositories.CartItems.DeleteById(c.Param("id"))
//
// if err != nil {
// data := CreateSessionData(c, gin.H{
// "error": err,
// "success": "",
// })
//
// c.HTML(http.StatusOK, "deleteitem.html", data)
// }
//
// cartItems, _ := repositories.CartItems.GetAll()
// fmt.Println(len(cartItems))
//
// data := CreateSessionData(c, gin.H{
// "title": "cartItem Page",
// "cartItems": shopItems,
// })
//
// fmt.Println(data)
//
// c.HTML(http.StatusOK, "index.html", data)
//}

View File

@@ -17,6 +17,7 @@ import(
var(
shopItemController controllers.ShopItemController = controllers.NewShopItemController()
userController controllers.UserController = controllers.UserController{}
cartItemController controllers.CartItemController = controllers.NewCartItemController()
authValidator middlewares.AuthValidator = middlewares.AuthValidator{}
)
@@ -84,6 +85,9 @@ func main() {
viewRoutes.GET("/tags", authValidator.RequireAuth, shopItemController.TagView)
viewRoutes.POST("/tags/:id", authValidator.RequireAuth, shopItemController.TagHandler)
viewRoutes.POST("/tags", authValidator.RequireAuth, shopItemController.AddTagHandler)
viewRoutes.GET("/cart", cartItemController.CartItemView)
viewRoutes.POST("/cart", cartItemController.AddItemHandler)
//write middleware that redirects to homescreen on register/login/reset for logged in users
viewRoutes.GET("/login", userController.LoginView)
viewRoutes.GET("/logout", userController.Logout)

12
models/cart.go Normal file
View File

@@ -0,0 +1,12 @@
package models
import (
"gorm.io/gorm"
)
type CartItem struct {
gorm.Model
SessionId string `gorm:"index"`
ShopItemId int `gorm:"index"`
Quantity int
}

View File

@@ -0,0 +1,57 @@
package repositories
import(
"gorm.io/gorm"
"example.com/gin/test/models"
)
type CartItemRepository interface {
Create(models.CartItem) (models.CartItem, error)
GetAll() ([]models.CartItem, error)
GetAllBySession(string) ([]models.CartItem, error)
Update(models.CartItem) (models.CartItem, error)
}
type GORMCartItemRepository struct {
DB *gorm.DB
}
func NewGORMCartItemRepository(db *gorm.DB) CartItemRepository {
return &GORMCartItemRepository{
DB: db,
}
}
func (r *GORMCartItemRepository) Create(cartItem models.CartItem) (models.CartItem, error) {
result := r.DB.Create(&cartItem)
if result.Error != nil {
return models.CartItem{}, result.Error
}
return cartItem, nil
}
func (r *GORMCartItemRepository) GetAll() ([]models.CartItem, error) {
var cartItems []models.CartItem
result := r.DB.Find(&cartItems)
return cartItems, result.Error
}
func (r *GORMCartItemRepository) GetAllBySession(sessionId string) ([]models.CartItem, error) {
var cartItems []models.CartItem
result := r.DB.Where("session_id = ?", sessionId).Find(&cartItems)
return cartItems, result.Error
}
func (r *GORMCartItemRepository) Update(cartItem models.CartItem) (models.CartItem, error) {
result := r.DB.Save(&cartItem)
if result.Error != nil {
return models.CartItem{}, result.Error
}
return cartItem, nil
}

View File

@@ -12,6 +12,7 @@ var(
ShopItems ShopItemRepository
Users UserRepository
Tags TagRepository
CartItems CartItemRepository
)
func InitRepositories() {
@@ -20,7 +21,7 @@ func InitRepositories() {
panic("failed to connect to database")
}
err = db.AutoMigrate(&models.ShopItem{}, &models.User{}, &models.Tag{})
err = db.AutoMigrate(&models.ShopItem{}, &models.User{}, &models.Tag{}, &models.CartItem{})
if err != nil {
panic("failed to migrate database")
}
@@ -28,4 +29,5 @@ func InitRepositories() {
ShopItems = NewGORMShopItemRepository(db)
Users = NewGORMUserRepository(db)
Tags = NewGORMTagRepository(db)
CartItems = NewGORMCartItemRepository(db)
}

35
views/addtag.html Normal file
View File

@@ -0,0 +1,35 @@
{{ template "header.html" . }}
<div class="flex min-h-full flex-col justify-center px-6 py-12 lg:px-8">
<div class="sm:mx-auto sm:w-full sm:max-w-sm">
<img class="mx-auto h-10 w-auto" src="/static/img/circlea.png" alt="Your Company">
<h2 class="mt-10 text-center text-2xl/9 font-bold tracking-tight text-gray-900">Add Tag</h2>
</div>
<div class="mt-10 sm:mx-auto sm:w-full sm:max-w-sm">
<form class="space-y-6" action="#" method="POST" enctype="multipart/form-data">
<div>
<label for="name" class="block text-sm/6 font-medium text-gray-900">Name</label>
<div class="mt-2">
<input type="text" name="name" id="name" required class="block w-full rounded-md bg-white px-3 py-1.5 text-base text-gray-900 outline outline-1 -outline-offset-1 outline-gray-300 placeholder:text-gray-400 focus:outline focus:outline-2 focus:-outline-offset-2 focus:outline-indigo-600 sm:text-sm/6">
</div>
</div>
<p class="mt-10 text-center text-sm/6 text-red-500">
{{ .data.error }}
</p>
<p class="mt-10 text-center text-sm/6 text-green-500">
{{ .data.success }}
</p>
<div>
<button type="submit" class="flex w-full justify-center rounded-md bg-indigo-600 px-3 py-1.5 text-sm/6 font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600">Add Tag</button>
</div>
</form>
</div>
</div>
{{ template "footer.html" . }}

18
views/cart.html Normal file
View File

@@ -0,0 +1,18 @@
{{ template "header.html" . }}
{{ range .data.cartItems }}
<div class="group relative">
<div class="mt-4 flex justify-between">
<div>
<h3 class="text-sm text-gray-700">
{{ .ShopItemId }}
</h3>
</div>
<p class="text-sm font-medium text-gray-900">{{ .Quantity }} pieces</p>
</div>
</div>
{{ end }}
{{ template "footer.html" . }}

View File

@@ -33,8 +33,7 @@
</div>
{{ else }}
<div class="absolute inset-y-0 right-0 flex items-center pr-2 sm:static sm:inset-auto sm:ml-6 sm:pr-0 px-4 sm:px-8">
<a href="/login" class="rounded-md bg-gray-900 m-2 px-3 py-2 text-sm font-medium text-gray-300 hover:bg-gray-700 hover:text-white">Login</a>
<a href="/register" class="rounded-md bg-gray-900 m-2 px-3 py-2 text-sm font-medium text-gray-300 hover:bg-gray-700 hover:text-white">Register</a>
<a href="/cart" class="rounded-md bg-gray-900 m-2 px-3 py-2 text-sm font-medium text-gray-300 hover:bg-gray-700 hover:text-white">&#128722; Cart</a>
</div>
{{ end }}
</div>

View File

@@ -8,9 +8,12 @@
<img class="w-full h-full object-cover" src="/{{ .data.shopItem.Image}}" alt="Product Image">
</div>
<div class="flex -mx-2 mb-4">
<form action="/cart" method="POST">
<input type="hidden" id="{{ .data.shopItem.ID}}" name="ShopItemId" value="{{ .data.shopItem.ID }}">
<div class="w-1/3 px-2">
<button class="w-full bg-gray-900 dark:bg-gray-600 text-white py-2 px-4 rounded-full font-bold hover:bg-gray-800 dark:hover:bg-gray-700">Add to Cart</button>
<button type="submit" class="w-full bg-gray-900 dark:bg-gray-600 text-white py-2 px-4 rounded-full font-bold hover:bg-gray-800 dark:hover:bg-gray-700">Add to Cart</button>
</div>
</form>
{{ if .loggedIn }}
<div class="w-1/3 px-2">