-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
EF-81-83 Customer can view and add to cart
- Loading branch information
1 parent
701dabd
commit 87d7b4e
Showing
18 changed files
with
330 additions
and
24 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { Module } from '@nestjs/common' | ||
import { MongooseModule } from '@nestjs/mongoose' | ||
|
||
import { Cart, CartSchema } from '@cart/schemas/cart.schema' | ||
import { CartController } from '@cart/controllers/cart.controller' | ||
import { CartService } from '@cart/services/cart.service' | ||
import { CartRepository } from '@cart/repositories/cart.repository' | ||
|
||
@Module({ | ||
imports: [MongooseModule.forFeature([{ name: Cart.name, schema: CartSchema }])], | ||
controllers: [CartController], | ||
providers: [CartService, CartRepository], | ||
exports: [CartService] | ||
}) | ||
export class CartModule {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import { Body, Controller, Get, Param, Patch, Post, Req, UseGuards } from '@nestjs/common' | ||
import { ApiBadRequestResponse, ApiBearerAuth, ApiOkResponse, ApiTags } from '@nestjs/swagger' | ||
import * as _ from 'lodash' | ||
|
||
import { ErrorResponse, ResponseSuccessDto } from '@common/contracts/dto' | ||
import { CartService } from '@cart/services/cart.service' | ||
import { JwtAuthGuard } from '@auth/guards/jwt-auth.guard' | ||
import { RolesGuard } from '@auth/guards/roles.guard' | ||
import { UserRole } from '@common/contracts/constant' | ||
import { Roles } from '@auth/decorators/roles.decorator' | ||
import { AddToCartDto, ResponseCartDto } from '@cart/dto/cart.dto' | ||
|
||
@ApiTags('Cart') | ||
@ApiBearerAuth() | ||
@Roles(UserRole.CUSTOMER) | ||
@UseGuards(JwtAuthGuard.ACCESS_TOKEN, RolesGuard) | ||
@Controller('cart') | ||
export class CartController { | ||
constructor(private readonly cartService: CartService) {} | ||
|
||
@Post() | ||
@ApiOkResponse({ type: ResponseSuccessDto }) | ||
@ApiBadRequestResponse({ type: ErrorResponse }) | ||
async addToCart(@Req() req, @Body() addToCartDto: AddToCartDto) { | ||
addToCartDto.customerId = _.get(req, 'user._id') | ||
const customer = await this.cartService.addToCart(addToCartDto) | ||
return customer | ||
} | ||
|
||
@Get() | ||
@ApiOkResponse({ type: ResponseCartDto }) | ||
async getListCard(@Req() req) { | ||
const customerId = _.get(req, 'user._id') | ||
return await this.cartService.getListCard(customerId) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { Cart, ItemDto } from '@cart/schemas/cart.schema' | ||
import { ApiProperty } from '@nestjs/swagger' | ||
|
||
export class AddToCartDto extends ItemDto { | ||
customerId?: string | ||
} | ||
|
||
export class ResponseCartDto { | ||
@ApiProperty() | ||
data: Cart | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { PaginateModel } from 'mongoose' | ||
import { Injectable } from '@nestjs/common' | ||
import { InjectModel } from '@nestjs/mongoose' | ||
|
||
import { AbstractRepository } from '@common/repositories' | ||
import { Cart, CartDocument } from '@cart/schemas/cart.schema' | ||
|
||
@Injectable() | ||
export class CartRepository extends AbstractRepository<CartDocument> { | ||
constructor(@InjectModel(Cart.name) model: PaginateModel<CartDocument>) { | ||
super(model) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose' | ||
import { HydratedDocument } from 'mongoose' | ||
import * as paginate from 'mongoose-paginate-v2' | ||
import { ApiProperty } from '@nestjs/swagger' | ||
import { Transform, Type } from 'class-transformer' | ||
import { Status } from '@common/contracts/constant' | ||
import { IsNotEmpty, Min, ValidateNested } from 'class-validator' | ||
|
||
import { Product } from '@product/schemas/product.schema' | ||
|
||
export class ItemDto { | ||
@ApiProperty() | ||
@IsNotEmpty() | ||
@ValidateNested() | ||
product: Product | ||
|
||
@ApiProperty() | ||
@IsNotEmpty() | ||
@Min(1) | ||
quantity: number | ||
} | ||
|
||
export type CartDocument = HydratedDocument<Cart> | ||
|
||
@Schema({ | ||
collection: 'carts', | ||
timestamps: { | ||
createdAt: true, | ||
updatedAt: true | ||
}, | ||
toJSON: { | ||
transform(doc, ret) { | ||
delete ret.__v | ||
} | ||
} | ||
}) | ||
export class Cart { | ||
constructor(id?: string) { | ||
this._id = id | ||
} | ||
|
||
@ApiProperty() | ||
@Transform(({ value }) => value?.toString()) | ||
_id: string | ||
|
||
@Prop({ type: String, required: true }) | ||
customerId: string; | ||
|
||
@ApiProperty({ isArray: true, type: ItemDto }) | ||
@ValidateNested() | ||
@Prop({ type: Array<ItemDto>, required: true }) | ||
items: ItemDto[] | ||
|
||
@ApiProperty() | ||
@Prop({ type: Number, required: true }) | ||
totalAmount: number | ||
|
||
@Prop({ | ||
enum: Status, | ||
default: Status.ACTIVE | ||
}) | ||
status: Status | ||
} | ||
|
||
export const CartSchema = SchemaFactory.createForClass(Cart) | ||
|
||
CartSchema.plugin(paginate) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
import { BadRequestException, Injectable } from '@nestjs/common' | ||
import { Errors } from '@src/common/contracts/error' | ||
import { CartRepository } from '@cart/repositories/cart.repository' | ||
import { AddToCartDto } from '@cart/dto/cart.dto' | ||
|
||
@Injectable() | ||
export class CartService { | ||
constructor(private readonly cartRepository: CartRepository) {} | ||
|
||
public async addToCart(addToCartDto: AddToCartDto) { | ||
const { customerId, product, quantity } = addToCartDto | ||
const cart = await this.cartRepository.findOne({ | ||
conditions: { | ||
customerId | ||
} | ||
}) | ||
const amount = product.price * quantity | ||
if (!cart) { | ||
// create new cart | ||
await this.cartRepository.create({ | ||
customerId, | ||
items: [{ product, quantity }], | ||
totalAmount: amount | ||
}) | ||
} else { | ||
const { _id, items } = cart | ||
// update cart | ||
// check existed item | ||
const existedItemIndex = items.findIndex((item) => item.product._id === product._id) | ||
|
||
if (existedItemIndex === -1) { | ||
// push new item in the first element | ||
items.unshift({ product, quantity }) | ||
} else { | ||
// update quantity in existed item | ||
items[existedItemIndex].quantity += quantity | ||
} | ||
const totalAmount = (cart.totalAmount += amount) | ||
await this.cartRepository.findOneAndUpdate( | ||
{ | ||
_id | ||
}, | ||
{ | ||
items, | ||
totalAmount | ||
} | ||
) | ||
} | ||
return { success: true } | ||
} | ||
|
||
public async getListCard(customerId: string) { | ||
const cartList = await this.cartRepository.findOne({ | ||
conditions: { customerId }, | ||
projection: { | ||
_id: 1, | ||
items: 1, | ||
totalAmount: 1 | ||
} | ||
}) | ||
if (!cartList) { | ||
const newCartList = await this.cartRepository.create({ | ||
customerId, | ||
items: [], | ||
totalAmount: 0 | ||
}) | ||
return { _id: newCartList._id, items: [], totalAmount: 0 } | ||
} | ||
return cartList | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import { ExecutionContext, createParamDecorator } from '@nestjs/common' | ||
import * as _ from 'lodash' | ||
|
||
export interface PaginationParams { | ||
page: number | ||
limit: number | ||
sort: Record<string, any> | ||
} | ||
|
||
const DEFAULT_LIMIT = 10 | ||
|
||
export const handlePagination: (request: any) => PaginationParams = (request) => { | ||
const paginationParams = { | ||
page: request.query.page, | ||
limit: request.query.limit, | ||
sort: request.query.sort | ||
} | ||
paginationParams.page = Number(paginationParams.page) > 0 ? Number(paginationParams.page) : 1 | ||
paginationParams.limit = Number(paginationParams.limit) > 0 ? Number(paginationParams.limit) : DEFAULT_LIMIT | ||
if (_.isEmpty(paginationParams.sort)) { | ||
paginationParams.sort = { | ||
createdAt: -1 | ||
} | ||
} else { | ||
const result = {} | ||
const sortFields = paginationParams.sort.split('_') | ||
sortFields.forEach((item) => { | ||
const sortType = item.indexOf('.asc') !== -1 ? '.asc' : '.desc' | ||
result[item.replace(sortType, '')] = sortType === '.asc' ? 1 : -1 | ||
}) | ||
paginationParams.sort = result | ||
} | ||
return paginationParams | ||
} | ||
|
||
/** | ||
* How to use | ||
- In controller: | ||
@ApiQuery({ type: PaginationQuery }) | ||
@Get() | ||
async list( | ||
@Pagination() paginationParams: PaginationParams, | ||
@Query() filterDto: FilterDto, | ||
) { | ||
return await this.service.list(filterDto, paginationParams); | ||
} | ||
- In service: | ||
async list(filterDto, paginationParams) { | ||
return await this.service.list(filterDto, paginationParams); | ||
} | ||
*/ | ||
export const Pagination = createParamDecorator((data: unknown, ctx: ExecutionContext) => { | ||
const request = ctx.switchToHttp().getRequest() | ||
return handlePagination(request) | ||
}) |
Oops, something went wrong.