Compare commits
	
		
			2 Commits 
		
	
	
		
			f190090be1
			...
			9e665f8710
		
	
	| Author | SHA1 | Date | 
|---|---|---|
| 
							
							
								 | 
						9e665f8710 | 10 months ago | 
| 
							
							
								 | 
						47910e1ac6 | 10 months ago | 
				 7 changed files with 219 additions and 93 deletions
			
			
		@ -0,0 +1,62 @@ | 
				
			|||||||
 | 
					'use strict'; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = { | 
				
			||||||
 | 
					  up: async (queryInterface, Sequelize) => { | 
				
			||||||
 | 
					    await queryInterface.createTable('Carts', { | 
				
			||||||
 | 
					      id: { | 
				
			||||||
 | 
					        type: Sequelize.INTEGER, | 
				
			||||||
 | 
					        allowNull: false, | 
				
			||||||
 | 
					        autoIncrement: true, | 
				
			||||||
 | 
					        primaryKey: true, | 
				
			||||||
 | 
					      }, | 
				
			||||||
 | 
					      userId: { | 
				
			||||||
 | 
					        type: Sequelize.INTEGER, | 
				
			||||||
 | 
					        allowNull: false, | 
				
			||||||
 | 
					        references: { | 
				
			||||||
 | 
					          model: 'Users', | 
				
			||||||
 | 
					          key: 'id', | 
				
			||||||
 | 
					        }, | 
				
			||||||
 | 
					        onDelete: 'CASCADE', | 
				
			||||||
 | 
					      }, | 
				
			||||||
 | 
					      productId: { | 
				
			||||||
 | 
					        type: Sequelize.INTEGER, | 
				
			||||||
 | 
					        allowNull: false, | 
				
			||||||
 | 
					        references: { | 
				
			||||||
 | 
					          model: 'Products', 
 | 
				
			||||||
 | 
					          key: 'id', | 
				
			||||||
 | 
					        }, | 
				
			||||||
 | 
					        onDelete: 'CASCADE', | 
				
			||||||
 | 
					      }, | 
				
			||||||
 | 
					      quantity: { | 
				
			||||||
 | 
					        type: Sequelize.INTEGER, | 
				
			||||||
 | 
					        allowNull: false, | 
				
			||||||
 | 
					      }, | 
				
			||||||
 | 
					      productPrice: { | 
				
			||||||
 | 
					        type: Sequelize.DECIMAL(10, 2), | 
				
			||||||
 | 
					        allowNull: false, | 
				
			||||||
 | 
					      }, | 
				
			||||||
 | 
					      totalPrice: { | 
				
			||||||
 | 
					        type: Sequelize.DECIMAL(10, 2), | 
				
			||||||
 | 
					        allowNull: false, | 
				
			||||||
 | 
					      }, | 
				
			||||||
 | 
					      productName: { | 
				
			||||||
 | 
					        type: Sequelize.STRING, | 
				
			||||||
 | 
					        allowNull: false, | 
				
			||||||
 | 
					      }, | 
				
			||||||
 | 
					      createdAt: { | 
				
			||||||
 | 
					        type: Sequelize.DATE, | 
				
			||||||
 | 
					        allowNull: false, | 
				
			||||||
 | 
					        defaultValue: Sequelize.NOW, | 
				
			||||||
 | 
					      }, | 
				
			||||||
 | 
					      updatedAt: { | 
				
			||||||
 | 
					        type: Sequelize.DATE, | 
				
			||||||
 | 
					        allowNull: false, | 
				
			||||||
 | 
					        defaultValue: Sequelize.NOW, | 
				
			||||||
 | 
					      }, | 
				
			||||||
 | 
					    }); | 
				
			||||||
 | 
					  }, | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  down: async (queryInterface, Sequelize) => { | 
				
			||||||
 | 
					    await queryInterface.dropTable('Carts'); | 
				
			||||||
 | 
					  }, | 
				
			||||||
 | 
					}; | 
				
			||||||
@ -1,26 +1,46 @@ | 
				
			|||||||
import { Controller, Get, Post, Body, Patch, Param, Delete } from "@nestjs/common"; | 
					import { Controller, Get, Post, Patch, Delete, Body, Param, UseGuards, Request } from "@nestjs/common"; | 
				
			||||||
import { CartService } from "./cart.service"; | 
					import { CartService } from "./cart.service"; | 
				
			||||||
 | 
					import { JwtAuthGuard } from "src/guard/auth.guard"; | 
				
			||||||
 | 
					import { AddToCartDto } from "./dto/add-to-cart.dto"; | 
				
			||||||
 | 
					import { UpdateCartDto } from "./dto/update-cart.dto"; | 
				
			||||||
import { Cart } from "./entities/cart.entity"; | 
					import { Cart } from "./entities/cart.entity"; | 
				
			||||||
 | 
					
 | 
				
			||||||
@Controller("cart") | 
					@Controller("cart") | 
				
			||||||
export class CartController { | 
					export class CartController { | 
				
			||||||
  constructor(private readonly cartService: CartService) {} | 
					  constructor(private readonly cartService: CartService) {} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @UseGuards(JwtAuthGuard) | 
				
			||||||
  @Post() | 
					  @Post() | 
				
			||||||
  async addToCart(@Body() body: { userId: number; productId: number; quantity: number }): Promise<{ message: string; cartItem: Cart }> { | 
					  async addToCart(@Body() addToCartDto: AddToCartDto, @Request() req: any): Promise<{ message: string; cartItem: Cart }> { | 
				
			||||||
    const { userId, productId, quantity } = body; | 
					    const userId = req.user.id; | 
				
			||||||
    const result = await this.cartService.addToCart(userId, productId, quantity); | 
					    return this.cartService.addToCart({ ...addToCartDto, userId }); | 
				
			||||||
    return result; | 
					 | 
				
			||||||
  } | 
					  } | 
				
			||||||
  @Get(":userId") | 
					
 | 
				
			||||||
  async getUserCart(@Param("userId") userId: number) { | 
					  @UseGuards(JwtAuthGuard) | 
				
			||||||
 | 
					  @Get() | 
				
			||||||
 | 
					  async getUserCart(@Request() req: any): Promise<{ cartItems: Cart[]; totalPrice: number }> { | 
				
			||||||
 | 
					    const userId = req.user.id; | 
				
			||||||
    return this.cartService.getUserCart(userId); | 
					    return this.cartService.getUserCart(userId); | 
				
			||||||
  } | 
					  } | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Delete(":userId/:productId") | 
					  @UseGuards(JwtAuthGuard) | 
				
			||||||
  async removeFromCart( | 
					  @Patch(":productId") | 
				
			||||||
    @Param("userId") userId: number, | 
					  async updateCart(@Param("productId") productId: number, @Body() updateCartDto: UpdateCartDto, @Request() req: any): Promise<{ message: string; updatedCart: Cart }> { | 
				
			||||||
    @Param("productId") productId: number, 
 | 
					    const userId = req.user.id; | 
				
			||||||
  ): Promise<{ message: string }> { | 
					    const updatedCart = await this.cartService.updateCart(userId, productId, updateCartDto.quantity); | 
				
			||||||
    return this.cartService.removeFromCart(userId, productId); | 
					    return { | 
				
			||||||
 | 
					      message: "Cart updated successfully", | 
				
			||||||
 | 
					      updatedCart, | 
				
			||||||
 | 
					    }; | 
				
			||||||
 | 
					  } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @UseGuards(JwtAuthGuard) | 
				
			||||||
 | 
					  @Delete(":productId") | 
				
			||||||
 | 
					  async removeFromCart(@Param("productId") productId: number, @Request() req: any): Promise<{ message: string }> { | 
				
			||||||
 | 
					    const userId = req.user.id; | 
				
			||||||
 | 
					    await this.cartService.removeFromCart(userId, productId); | 
				
			||||||
 | 
					    return { | 
				
			||||||
 | 
					      message: "Product removed from cart successfully", | 
				
			||||||
 | 
					    }; | 
				
			||||||
  } | 
					  } | 
				
			||||||
} | 
					} | 
				
			||||||
 | 
				
			|||||||
@ -1,101 +1,98 @@ | 
				
			|||||||
import { Injectable } from "@nestjs/common"; | 
					import { Injectable, HttpException, HttpStatus } from "@nestjs/common"; | 
				
			||||||
import { InjectModel } from "@nestjs/sequelize"; | 
					import { InjectModel } from "@nestjs/sequelize"; | 
				
			||||||
import { Cart } from "./entities/cart.entity"; | 
					import { Cart } from "./entities/cart.entity"; | 
				
			||||||
import { HttpException, HttpStatus } from "@nestjs/common"; | 
					 | 
				
			||||||
import { CartResponse } from "./cart.response"; | 
					import { CartResponse } from "./cart.response"; | 
				
			||||||
import { User } from "src/users/entities/user.entity"; | 
					import { User } from "src/users/entities/user.entity"; | 
				
			||||||
import { Product } from "src/products/entities/product.entity"; | 
					import { Product } from "src/products/entities/product.entity"; | 
				
			||||||
 | 
					import { console } from "inspector"; | 
				
			||||||
 | 
					
 | 
				
			||||||
@Injectable() | 
					@Injectable() | 
				
			||||||
export class CartService { | 
					export class CartService { | 
				
			||||||
  constructor( | 
					  constructor( | 
				
			||||||
    @InjectModel(Cart) private readonly cartModel: typeof Cart, | 
					    @InjectModel(Cart) private readonly cartModel: typeof Cart, | 
				
			||||||
    @InjectModel(User) private readonly userModel: typeof User, | 
					 | 
				
			||||||
    @InjectModel(Product) private readonly productModel: typeof Product, | 
					    @InjectModel(Product) private readonly productModel: typeof Product, | 
				
			||||||
  ) {} | 
					  ) {} | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  async addToCart(userId: number, productId: number, quantity: number): Promise<CartResponse> { | 
					  // Add product to cart
 | 
				
			||||||
    try { | 
					  async addToCart(addToCartDto: { userId: number; productId: number; quantity: number }): Promise<{ message: string; cartItem: Cart }> { | 
				
			||||||
      if (!userId || !productId || !quantity) { | 
					    const { userId, productId, quantity } = addToCartDto; | 
				
			||||||
        throw new HttpException("Missing required parameters: userId, productId, and quantity are required.", HttpStatus.BAD_REQUEST); | 
					
 | 
				
			||||||
      } | 
					    if (!userId || !productId || !quantity) { | 
				
			||||||
 | 
					      throw new HttpException("Missing required parameters: userId, productId, and quantity are required.", HttpStatus.BAD_REQUEST); | 
				
			||||||
      const user = await this.userModel.findByPk(userId); | 
					    } | 
				
			||||||
      if (!user) { | 
					
 | 
				
			||||||
        throw new HttpException("User not found!", HttpStatus.NOT_FOUND); | 
					    const product = await this.productModel.findByPk(productId); | 
				
			||||||
      } | 
					    if (!product) { | 
				
			||||||
 | 
					      throw new HttpException("Product not found!", HttpStatus.NOT_FOUND); | 
				
			||||||
      const product = await this.productModel.findByPk(productId); | 
					    } | 
				
			||||||
      if (!product) { | 
					
 | 
				
			||||||
        throw new HttpException("Product not found!", HttpStatus.NOT_FOUND); | 
					    const existingCartItem = await this.cartModel.findOne({ | 
				
			||||||
      } | 
					      where: { userId, productId }, | 
				
			||||||
 | 
					    }); | 
				
			||||||
      const existingCartItem = await this.cartModel.findOne({ | 
					
 | 
				
			||||||
        where: { userId, productId }, | 
					    if (existingCartItem) { | 
				
			||||||
      }); | 
					      existingCartItem.quantity += Number(quantity); | 
				
			||||||
 | 
					      existingCartItem.totalPrice = existingCartItem.quantity * existingCartItem.productPrice; | 
				
			||||||
      if (existingCartItem) { | 
					      await existingCartItem.save(); | 
				
			||||||
        existingCartItem.quantity += Number(quantity); | 
					 | 
				
			||||||
        await existingCartItem.save(); | 
					 | 
				
			||||||
        return { | 
					 | 
				
			||||||
          message: "Product quantity updated in cart successfully!", | 
					 | 
				
			||||||
          cartItem: existingCartItem, | 
					 | 
				
			||||||
        }; | 
					 | 
				
			||||||
      } | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      const newCartItem = await this.cartModel.create({ userId, productId, quantity }); | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      return { | 
					      return { | 
				
			||||||
        message: "Product added to cart successfully!", | 
					        message: "Product quantity updated in cart successfully!", | 
				
			||||||
        cartItem: newCartItem, | 
					        cartItem: existingCartItem, | 
				
			||||||
      }; | 
					      }; | 
				
			||||||
    } catch (error) { | 
					 | 
				
			||||||
      // Enhanced error handling
 | 
					 | 
				
			||||||
      if (error instanceof HttpException) { | 
					 | 
				
			||||||
        throw error; | 
					 | 
				
			||||||
      } | 
					 | 
				
			||||||
      throw new HttpException(`An error occurred while adding the product to the cart: ${error.message}`, HttpStatus.INTERNAL_SERVER_ERROR); | 
					 | 
				
			||||||
    } | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const newCartItem = await this.cartModel.create({ userId, productId, quantity, productPrice: product.price, totalPrice: product.price * quantity, productName: product.name }); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return { | 
				
			||||||
 | 
					      message: "Product added to cart successfully!", | 
				
			||||||
 | 
					      cartItem: newCartItem, | 
				
			||||||
 | 
					    }; | 
				
			||||||
  } | 
					  } | 
				
			||||||
  async getUserCart(userId: number): Promise<Cart[]> { | 
					
 | 
				
			||||||
    try { | 
					  // Get user's cart
 | 
				
			||||||
      const cartItems = await this.cartModel.findAll({ | 
					  async getUserCart(userId: number): Promise<{ cartItems: Cart[]; totalPrice: number }> { | 
				
			||||||
        where: { userId }, | 
					    const cartItems = await this.cartModel.findAll({ | 
				
			||||||
        include: ["product"], | 
					      where: { userId }, | 
				
			||||||
      }); | 
					      include: [ | 
				
			||||||
      if (!cartItems || cartItems.length === 0) { | 
					        { | 
				
			||||||
        throw new HttpException("No cart items found for the specified user.", HttpStatus.NOT_FOUND); | 
					          model: Product, | 
				
			||||||
      } | 
					          attributes: ["id", "name", "price", "description", "imageUrl"], | 
				
			||||||
 | 
					        }, | 
				
			||||||
      return cartItems; | 
					      ], | 
				
			||||||
    } catch (error) { | 
					    }); | 
				
			||||||
      if (error instanceof HttpException) { | 
					
 | 
				
			||||||
        throw error; | 
					    if (!cartItems || cartItems.length === 0) { | 
				
			||||||
      } | 
					      throw new HttpException("No cart items found for the specified user.", HttpStatus.NOT_FOUND); | 
				
			||||||
 | 
					 | 
				
			||||||
      throw new HttpException("An error occurred while retrieving the cart.", HttpStatus.INTERNAL_SERVER_ERROR); | 
					 | 
				
			||||||
    } | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const totalPrice = cartItems.reduce((sum, item) => sum + Number(item.totalPrice), 0); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return { cartItems, totalPrice }; | 
				
			||||||
  } | 
					  } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Update cart item quantity
 | 
				
			||||||
 | 
					  async updateCart(userId: number, productId: number, quantity: number): Promise<Cart> { | 
				
			||||||
 | 
					    const cartItem = await this.cartModel.findOne({ where: { userId, productId } }); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!cartItem) { | 
				
			||||||
 | 
					      throw new HttpException("Product not found in the cart.", HttpStatus.NOT_FOUND); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    cartItem.quantity = quantity; | 
				
			||||||
 | 
					    cartItem.totalPrice = cartItem.quantity * cartItem.productPrice; | 
				
			||||||
 | 
					    await cartItem.save(); | 
				
			||||||
 | 
					    return cartItem; | 
				
			||||||
 | 
					  } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Remove product from cart
 | 
				
			||||||
  async removeFromCart(userId: number, productId: number): Promise<{ message: string }> { | 
					  async removeFromCart(userId: number, productId: number): Promise<{ message: string }> { | 
				
			||||||
    try { | 
					    const cartItem = await this.cartModel.findOne({ where: { userId, productId } }); | 
				
			||||||
      const cartItem = await this.cartModel.findOne({ where: { userId, productId } }); | 
					
 | 
				
			||||||
  
 | 
					    if (!cartItem) { | 
				
			||||||
      if (!cartItem) { | 
					      throw new HttpException("Product not found in the cart.", HttpStatus.NOT_FOUND); | 
				
			||||||
        throw new HttpException('Product not found in the cart.', HttpStatus.NOT_FOUND); | 
					 | 
				
			||||||
      } | 
					 | 
				
			||||||
  
 | 
					 | 
				
			||||||
      await cartItem.destroy(); | 
					 | 
				
			||||||
      return { message: 'Item deleted from your cart successfully.' }; | 
					 | 
				
			||||||
    } catch (error) { | 
					 | 
				
			||||||
      if (error instanceof HttpException) { | 
					 | 
				
			||||||
        throw error; | 
					 | 
				
			||||||
      } | 
					 | 
				
			||||||
  
 | 
					 | 
				
			||||||
      throw new HttpException( | 
					 | 
				
			||||||
        'An error occurred while removing the product from the cart.', | 
					 | 
				
			||||||
        HttpStatus.INTERNAL_SERVER_ERROR, | 
					 | 
				
			||||||
      ); | 
					 | 
				
			||||||
    } | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    await cartItem.destroy(); | 
				
			||||||
 | 
					    return { message: "Item deleted from your cart successfully." }; | 
				
			||||||
  } | 
					  } | 
				
			||||||
} | 
					} | 
				
			||||||
 | 
				
			|||||||
@ -0,0 +1,13 @@ | 
				
			|||||||
 | 
					// add-to-cart.dto.ts
 | 
				
			||||||
 | 
					import { IsInt, IsNotEmpty, IsNumber, min, Min } from 'class-validator'; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class AddToCartDto { | 
				
			||||||
 | 
					  @IsNumber()  
 | 
				
			||||||
 | 
					  @IsNotEmpty() | 
				
			||||||
 | 
					  productId: number; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @IsInt() | 
				
			||||||
 | 
					  @IsNotEmpty() | 
				
			||||||
 | 
					  @Min(0) | 
				
			||||||
 | 
					  quantity: number; | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,8 @@ | 
				
			|||||||
 | 
					// update-cart.dto.ts
 | 
				
			||||||
 | 
					import { IsInt, Min } from 'class-validator'; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class UpdateCartDto { | 
				
			||||||
 | 
					  @IsInt() | 
				
			||||||
 | 
					  @Min(1) | 
				
			||||||
 | 
					  quantity: number; | 
				
			||||||
 | 
					} | 
				
			||||||
					Loading…
					
					
				
		Reference in new issue