package controllers import ( database "ares/database/config" "ares/database/models" "errors" "net/http" "strconv" "github.com/gofiber/fiber/v3" "gorm.io/gorm" ) // getOrCreateCart is a helper to fetch the cart of the current user. func getOrCreateCart(userID uint) (models.Cart, error) { var cart models.Cart if err := database.DB.Preload("Items").Preload("Items.Product").Where("user_id = ?", userID).First(&cart).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { cart = models.Cart{UserID: userID} if createErr := database.DB.Create(&cart).Error; createErr != nil { return cart, createErr } return cart, nil } return cart, err } return cart, nil } // GetMyCart godoc // @Summary Get the current user's cart // @Tags Cart // @Produce json // @Security BearerAuth // @Success 200 {object} models.CartDoc // @Failure 401 {object} map[string]string // @Router /api/v1/cart [get] func GetMyCart(c fiber.Ctx) error { userID, ok := c.Locals("user_id").(uint) if !ok || userID == 0 { return c.Status(http.StatusUnauthorized).JSON(fiber.Map{"error": "unauthorized"}) } cart, err := getOrCreateCart(userID) if err != nil { return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": "could not load cart"}) } return c.JSON(cart) } // AddToCartRequest represents the body for adding to a cart type AddToCartRequest struct { ProductID uint `json:"product_id" validate:"required"` Quantity int `json:"quantity" validate:"required,min=1"` } // AddToCart godoc // @Summary Add item to cart // @Tags Cart // @Accept json // @Produce json // @Security BearerAuth // @Param data body AddToCartRequest true "Cart Item Details" // @Success 200 {object} models.CartDoc // @Failure 400 {object} map[string]string // @Failure 401 {object} map[string]string // @Failure 404 {object} map[string]string // @Router /api/v1/cart/items [post] func AddToCart(c fiber.Ctx) error { userID, ok := c.Locals("user_id").(uint) if !ok || userID == 0 { return c.Status(http.StatusUnauthorized).JSON(fiber.Map{"error": "unauthorized"}) } var input AddToCartRequest if err := c.Bind().Body(&input); err != nil { return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": "invalid payload"}) } if err := validate.Struct(input); err != nil { return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": err.Error()}) } // Check product exists var product models.Product if err := database.DB.First(&product, input.ProductID).Error; err != nil { return c.Status(http.StatusNotFound).JSON(fiber.Map{"error": "product not found"}) } cart, err := getOrCreateCart(userID) if err != nil { return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": "could not load cart"}) } // Check if item already exists in cart var existingItem models.CartItem if err := database.DB.Where("cart_id = ? AND product_id = ?", cart.ID, input.ProductID).First(&existingItem).Error; err == nil { // Update quantity existingItem.Quantity += input.Quantity database.DB.Save(&existingItem) } else { // Create new item newItem := models.CartItem{ CartID: cart.ID, ProductID: input.ProductID, Quantity: input.Quantity, } database.DB.Create(&newItem) } // Return updated cart cart, _ = getOrCreateCart(userID) return c.JSON(cart) } // UpdateCartItemRequest represents the body for updating a cart item quantity type UpdateCartItemRequest struct { Quantity int `json:"quantity" validate:"required,min=1"` } // UpdateCartItem godoc // @Summary Update cart item quantity // @Tags Cart // @Accept json // @Produce json // @Security BearerAuth // @Param item_id path int true "Cart Item ID" // @Param data body UpdateCartItemRequest true "Update Quantity" // @Success 200 {object} models.CartDoc // @Failure 400 {object} map[string]string // @Failure 401 {object} map[string]string // @Failure 404 {object} map[string]string // @Router /api/v1/cart/items/{item_id} [put] func UpdateCartItem(c fiber.Ctx) error { userID, ok := c.Locals("user_id").(uint) if !ok || userID == 0 { return c.Status(http.StatusUnauthorized).JSON(fiber.Map{"error": "unauthorized"}) } itemID, err := strconv.ParseUint(c.Params("item_id"), 10, 32) if err != nil { return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": "invalid item id"}) } var input UpdateCartItemRequest if err := c.Bind().Body(&input); err != nil { return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": "invalid payload"}) } if err := validate.Struct(input); err != nil { return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": err.Error()}) } cart, err := getOrCreateCart(userID) if err != nil { return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": "could not load cart"}) } var cartItem models.CartItem if err := database.DB.Where("id = ? AND cart_id = ?", itemID, cart.ID).First(&cartItem).Error; err != nil { return c.Status(http.StatusNotFound).JSON(fiber.Map{"error": "item not found in your cart"}) } cartItem.Quantity = input.Quantity database.DB.Save(&cartItem) // Return updated cart cart, _ = getOrCreateCart(userID) return c.JSON(cart) } // RemoveFromCart godoc // @Summary Remove item from cart // @Tags Cart // @Produce json // @Security BearerAuth // @Param item_id path int true "Cart Item ID" // @Success 200 {object} models.CartDoc // @Failure 400 {object} map[string]string // @Failure 401 {object} map[string]string // @Failure 404 {object} map[string]string // @Router /api/v1/cart/items/{item_id} [delete] func RemoveFromCart(c fiber.Ctx) error { userID, ok := c.Locals("user_id").(uint) if !ok || userID == 0 { return c.Status(http.StatusUnauthorized).JSON(fiber.Map{"error": "unauthorized"}) } itemID, err := strconv.ParseUint(c.Params("item_id"), 10, 32) if err != nil { return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": "invalid item id"}) } cart, err := getOrCreateCart(userID) if err != nil { return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": "could not load cart"}) } var cartItem models.CartItem if err := database.DB.Where("id = ? AND cart_id = ?", itemID, cart.ID).First(&cartItem).Error; err != nil { return c.Status(http.StatusNotFound).JSON(fiber.Map{"error": "item not found in your cart"}) } database.DB.Delete(&cartItem) // Return updated cart cart, _ = getOrCreateCart(userID) return c.JSON(cart) } // ClearCart godoc // @Summary Clear the entire cart // @Tags Cart // @Produce json // @Security BearerAuth // @Success 200 {object} models.CartDoc // @Failure 401 {object} map[string]string // @Router /api/v1/cart [delete] func ClearCart(c fiber.Ctx) error { userID, ok := c.Locals("user_id").(uint) if !ok || userID == 0 { return c.Status(http.StatusUnauthorized).JSON(fiber.Map{"error": "unauthorized"}) } cart, err := getOrCreateCart(userID) if err != nil { return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": "could not load cart"}) } // Delete all items for this cart database.DB.Where("cart_id = ?", cart.ID).Delete(&models.CartItem{}) // Return updated empty cart cart, _ = getOrCreateCart(userID) return c.JSON(cart) }