Skip to content

Commit

Permalink
Merge pull request #14 from Graduation-Thesis-K23/filter-product
Browse files Browse the repository at this point in the history
Filter product
  • Loading branch information
daihiep-swe authored Jun 23, 2023
2 parents b3a22ca + fbe6a2a commit 9473c4e
Show file tree
Hide file tree
Showing 11 changed files with 194 additions and 16 deletions.
12 changes: 11 additions & 1 deletion src/auth-manager/auth-manager.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import { SignInDto } from "./dto/sign-in.dto";
import { JwtEmployeeAuthGuard } from "./guards/jwt-employee.guards";
import { RolesGuard } from "./guards/role.guard";
import { Roles } from "./decorators/roles.decorator";
import { Role } from "~/shared";
import { EmployeePayload, Role } from "~/shared";
import { Public } from "~/auth/decorators";
import { ChangePasswordDto } from "./dto/change-password.dto";

@Controller("auth-manager")
export class AuthManagerController {
Expand All @@ -34,6 +35,15 @@ export class AuthManagerController {
return req.user;
}

@Post("change-password")
@UseGuards(JwtEmployeeAuthGuard, RolesGuard)
@Roles(Role.Branch, Role.Employee, Role.Manager, Role.Shipper)
async changePassword(@Req() req: Request, @Body() changePasswordDto: ChangePasswordDto) {
const { phone } = req.user as EmployeePayload;

return this.authManagerService.changePassword(phone, changePasswordDto);
}

@Get("logout")
@UseGuards(JwtEmployeeAuthGuard, RolesGuard)
@Roles(Role.Admin, Role.Branch, Role.Employee, Role.Manager, Role.Shipper)
Expand Down
24 changes: 24 additions & 0 deletions src/auth-manager/auth-manager.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { JwtService } from "@nestjs/jwt";
import { SignInDto } from "./dto/sign-in.dto";
import { EmployeePayload } from "~/shared";
import { EmployeeService } from "./../employee/employee.service";
import { ChangePasswordDto } from "./dto/change-password.dto";

@Injectable()
export class AuthManagerService {
Expand Down Expand Up @@ -39,6 +40,29 @@ export class AuthManagerService {
return [access_token, payload];
}

async changePassword(phone: string, changePasswordDto: ChangePasswordDto): Promise<EmployeePayload> {
const employee = await this.employeeService.findOneByPhone(phone);

if (!employee) {
throw new UnauthorizedException("Account not exist");
}

const isMatch = await bcrypt.compare(changePasswordDto.password, employee.password);
if (!isMatch) {
throw new UnauthorizedException("Old password not correct");
}

employee.password = changePasswordDto.newPassword;

await this.employeeService.save(employee);

return {
phone: employee.phone,
name: employee.name,
role: employee.role,
};
}

private async signToken(payload: any): Promise<string> {
const options = {
secret: this.configService.get<string>("JWT_EMPLOYEE_SECRET"),
Expand Down
13 changes: 13 additions & 0 deletions src/auth-manager/dto/change-password.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { IsNotEmpty, IsString, MinLength } from "class-validator";

export class ChangePasswordDto {
@IsNotEmpty()
@IsString()
@MinLength(8)
password: string;

@IsNotEmpty()
@IsString()
@MinLength(8)
newPassword: string;
}
10 changes: 5 additions & 5 deletions src/employee/employee.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ import {
Query,
ParseEnumPipe,
Req,
BadRequestException,
BadGatewayException,
} from "@nestjs/common";
import { Request } from "express";

Expand Down Expand Up @@ -41,13 +39,15 @@ export class EmployeeController {
}
}

@Get("disable-employee/:id")
@Get("active-employee/:id")
@Roles(Role.Manager)
async disableEmployee(@Req() req: Request, @Param("id", new ParseUUIDPipe({ version: "4" })) id: string) {
async disableEmployee(@Req() req: Request, @Param("id") id: string, @Query("active") active: boolean) {
try {
const { phone } = req.user as EmployeePayload;
return await this.employeeService.disableEmployee(phone, id);

return await this.employeeService.disableEmployee(phone, id, active);
} catch (error) {
console.log(error);
await this.errorsService.save(error.response);
}
}
Expand Down
41 changes: 35 additions & 6 deletions src/employee/employee.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ export class EmployeeService {
return await this.employeeRepository.findOneBy({ phone });
}

async save(obj: unknown) {
return await this.employeeRepository.save(obj);
}

async createEmployee(phone: string, createEmployeeDto: CreateEmployeeDto) {
// check employee exist
const employeeExist = await this.findOneByPhone(createEmployeeDto.phone);
Expand Down Expand Up @@ -90,32 +94,57 @@ export class EmployeeService {
};
}

async disableEmployee(phone: string, id: string) {
async disableEmployee(phone: string, id: string, active: boolean) {
const branch: Branch = await this.getBranchIdByEmployeePhone(phone);

const employee = await this.employeeRepository.findOneBy({ id, branchId: branch });
const employee = await this.employeeRepository
.createQueryBuilder("employee")
.where("employee.phone = :id", { id })
.andWhere("employee.branchId = :branchId", { branchId: branch.id })
.getOne();

if (!employee) {
throw new BadRequestException(`Employee with branch ${branch.id} and id ${id} not exist`);
}

employee.isActive = false;
const newEmployee = new Employee({
...employee,
isActive: active,
});

await this.employeeRepository.save(newEmployee);

return await this.employeeRepository.save(employee);
return {
id: newEmployee.id,
name: newEmployee.name,
phone: newEmployee.phone,
created_at: newEmployee.created_at,
isActive: newEmployee.isActive,
};
}

async resetPasswordEmployee(phone: string, id: string) {
const branch: Branch = await this.getBranchIdByEmployeePhone(phone);

const employee = await this.employeeRepository.findOneBy({ id, branchId: branch });
const employee = await this.employeeRepository
.createQueryBuilder("employee")
.where("employee.id = :id", { id })
.andWhere("employee.branchId = :branchId", { branchId: branch.id })
.getOne();

if (!employee) {
throw new BadRequestException(`Employee with branch ${branch.id} and id ${id} not exist`);
}

employee.password = "Chainmart123@@"; // default password

return await this.employeeRepository.save(employee);
const newPassword = await this.employeeRepository.save(employee);

return {
id: newPassword.id,
name: newPassword.name,
phone: newPassword.phone,
};
}

async getBranchIdByEmployeePhone(phone: string): Promise<Branch> {
Expand Down
1 change: 0 additions & 1 deletion src/errors/errors.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ export class ErrorsService {
) {}

async save(error: ErrorType) {
console.log(error);
if (error.statusCode >= 400 && error.statusCode < 500) {
throw new BadRequestException(error.message);
}
Expand Down
32 changes: 32 additions & 0 deletions src/products/dto/search-and-filter.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Transform } from "class-transformer";
import { IsEnum, IsNumber, IsOptional, IsString } from "class-validator";

enum SortBy {
ASC = "asc",
DESC = "desc",
LATEST = "latest",
SALES = "sales",
}

export class SearchAndFilterQueryDto {
@IsString()
@IsOptional()
categories?: string;

@Transform(({ value }) => Number(value))
@IsNumber()
@IsOptional()
maxPrice?: number;

@Transform(({ value }) => Number(value))
@IsNumber()
@IsOptional()
minPrice?: number;

@IsEnum(SortBy)
@IsOptional()
orderBy?: SortBy;

@IsString()
keyword: string;
}
15 changes: 14 additions & 1 deletion src/products/products.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
HttpStatus,
BadRequestException,
UseGuards,
Query,
} from "@nestjs/common";
import { FilesInterceptor } from "@nestjs/platform-express";

Expand All @@ -25,10 +26,12 @@ import { RolesGuard } from "../auth-manager/guards/role.guard";
import { Public } from "../auth/decorators/public.decorator";
import { ProductListType, ProductType, Role } from "~/shared";
import { JwtEmployeeAuthGuard } from "~/auth-manager/guards/jwt-employee.guards";
import { SearchAndFilterQueryDto } from "./dto/search-and-filter.dto";
import { ErrorsService } from "~/errors/errors.service";

@Controller("products")
export class ProductsController {
constructor(private readonly productsService: ProductsService) {}
constructor(private readonly productsService: ProductsService, private readonly errorsService: ErrorsService) {}

@Post()
@UseGuards(JwtEmployeeAuthGuard, RolesGuard)
Expand Down Expand Up @@ -67,6 +70,16 @@ export class ProductsController {
return this.productsService.getAll();
}

@Get("search-and-filter")
@Public()
async searchAndFilter(@Query() query: SearchAndFilterQueryDto): Promise<ProductListType[]> {
try {
return this.productsService.searchAndFilter(query);
} catch (error) {
await this.errorsService.save(error.response);
}
}

@Get("search/:searchText")
@Public()
getSearchProduct(@Param("searchText") searchText: string): Promise<Product[]> {
Expand Down
3 changes: 2 additions & 1 deletion src/products/products.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import { ProductsService } from "./products.service";
import { ProductsController } from "./products.controller";
import { Product } from "./entities/product.entity";
import { S3Module } from "../s3/s3.module";
import { ErrorsModule } from "~/errors/errors.module";

@Module({
imports: [TypeOrmModule.forFeature([Product]), S3Module],
imports: [TypeOrmModule.forFeature([Product]), S3Module, ErrorsModule],
controllers: [ProductsController],
providers: [ProductsService],
})
Expand Down
57 changes: 57 additions & 0 deletions src/products/products.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { UpdateProductDto } from "./dto/update-product.dto";
import { Product } from "./entities/product.entity";
import { S3Service } from "../s3/s3.service";
import { ProductListType, ProductType } from "~/shared";
import { SearchAndFilterQueryDto } from "./dto/search-and-filter.dto";

class AddProductType extends CreateProductDto {
images: string;
Expand Down Expand Up @@ -70,6 +71,62 @@ export class ProductsService {
});
}

async searchAndFilter(query: SearchAndFilterQueryDto): Promise<ProductListType[]> {
const queryProperties = Object.keys(query);

const products = this.productRepository
.createQueryBuilder("products")
.select([
"products.id",
"products.name",
"products.price",
"products.images",
"products.created_at",
"products.slug",
"products.sold",
"products.rating",
"products.isHot",
"products.sale",
])
.where("products.name like :keyword", { keyword: `%${query.keyword}%` });

if (queryProperties.includes("categories")) {
const categories = query.categories.split(",");
products.andWhere("products.category IN (:...categories)", { categories });
}
if (queryProperties.includes("minPrice")) {
products.andWhere("products.price >= :minPrice", { minPrice: query.minPrice });
}

if (queryProperties.includes("maxPrice")) {
products.andWhere("products.price <= :maxPrice", { maxPrice: query.maxPrice });
}

if (queryProperties.includes("orderBy")) {
const orderBy = query.orderBy.toLowerCase();
// ["asc", "desc", "latest", "sales"]
if (orderBy === "asc" || orderBy === "desc") {
const order: "ASC" | "DESC" = orderBy === "asc" ? "ASC" : "DESC";
products.orderBy("products.price", order);
} else if (orderBy === "latest") {
products.orderBy("products.created_at", "DESC");
} else if (orderBy === "sales") {
products.orderBy("COALESCE(sale, 0)", "DESC");
}
}

// execute query
const result = await products.getMany();

return result.map((product) => {
return {
...product,
images: product.images.split(","),
created_at: product.created_at.toISOString(),
};
});
}

async getById(id: string): Promise<Product> {
return await this.productRepository.findOneBy({
id,
Expand Down
2 changes: 1 addition & 1 deletion src/shared
Submodule shared updated 4 files
+10 −10 categories.ts
+19 −0 interfaces.ts
+13 −10 locales/en.ts
+12 −10 locales/vi.ts

0 comments on commit 9473c4e

Please sign in to comment.